<template>
  <div class="app-container">
    <!-- 固定在顶部的步骤条和按钮组 -->
    <div class="fixed-header" style="z-index: 5">
      <el-steps
        class="steps-container"
        :space="200"
        :active="currentStep"
        simple
      >
        <el-step title="导入校正集" />
        <el-step title="数据预处理" />
        <el-step title="波长选择" />
        <el-step title="主成分分析" />
        <el-step title="训练模型" />
        <el-step title="保存模型" />
      </el-steps>

      <div class="button-group">
        <el-button
          type="primary"
          size="mini"
          @click="handleImportCalibration()"
          :disabled="currentStep < 0"
          >导入校正集</el-button
        >
        <el-button
          type="primary"
          size="mini"
          @click="handlePreProcess()"
          :disabled="currentStep < 1"
          >数据预处理</el-button
        >
        <el-button
          type="primary"
          size="mini"
          @click="handleWaveLengthChoose()"
          :disabled="currentStep < 2"
          >波长选择</el-button
        >
        <el-button
          type="primary"
          size="mini"
          @click="handlePrincipalComponentAnalysis()"
          :disabled="currentStep < 3"
          >主成分分析</el-button
        >
        <el-button
          type="primary"
          size="mini"
          @click="handleTraining()"
          :disabled="currentStep < 4"
          >训练模型</el-button
        >
        <el-button
          type="primary"
          size="mini"
          @click="saveModel()"
          :disabled="currentStep < 5"
          >保存模型</el-button
        >
        <el-button type="primary" plain size="small" @click="exportReport()"
          >导出建模报告</el-button
        >
        <!-- <el-button plain size="small" @click="closePage()">关闭</el-button> -->
        <!-- <el-button type="warning" plain size="small" @click="addModelDetail()"
          >存储</el-button
        >
        <el-button
          type="warning"
          plain
          size="small"
          @click="deleteModelDetail()"
          >删除</el-button
        >
        <el-button type="warning" plain size="small" @click="getModelDetail()"
          >查看</el-button
        > -->
        <!-- <el-button
          type="danger"
          plain
          size="small"
          @click="debugSavePreprocessing()"
          >调试-保存预处理</el-button
        >
        <el-button
          type="danger"
          plain
          size="small"
          @click="debugSaveWaveSelect()"
          >调试-保存波长选择</el-button
        > -->
      </div>
    </div>

    <!-- 可滚动的内容区域 -->
    <div class="content-area" >
      <el-card shadow="always" style="padding: 20px">
        <el-row :gutter="20">
          <el-col :span="8">
            <stacked-line-chart
              v-if="seriesData1.length > 0"
              title="校正集吸光度曲线"
              :xAxisData="xAxisData1"
              :seriesData="seriesData1"
            />
            <div v-else class="empty-chart-placeholder">
              <div class="chart-title">校正集吸光度曲线</div>
              <div class="placeholder-content">
                <i class="el-icon-info"></i>
                <p>请先导入校正集</p>
              </div>
            </div>
          </el-col>
          <el-col :span="8">
            <stacked-line-chart
              ref="preprocessChart"
              v-if="seriesData2.length > 0"
              title="预处理吸光度曲线"
              :xAxisData="xAxisData2"
              :seriesData="seriesData2"
            />
            <div v-else class="empty-chart-placeholder">
              <div class="chart-title">预处理吸光度曲线</div>
              <div class="placeholder-content">
                <i class="el-icon-info"></i>
                <p>请先完成数据预处理步骤</p>
              </div>
            </div>
          </el-col>
          <el-col :span="8">
            <div style="position: relative">
              <!-- 左上角悬浮按钮 -->
              <el-popover placement="bottom-end" width="220" trigger="click">
                <!-- 下拉选择框放在 Popover 内 -->
                <el-select
                  v-model="selectedGroups"
                  multiple
                  collapse-tags
                  collapse-tags-tooltip
                  filterable
                  placeholder="选择基团"
                  size="small"
                  style="width: 200px"
                  @change="handleGroupSelectionChange"
                >
                  <el-option
                    v-for="item in groupInformationList"
                    :key="item.id"
                    :label="`${item.name}: ${item.minWavelength}-${item.maxWavelength}`"
                    :value="item.id"
                  />
                </el-select>

                <!-- 触发按钮 + 悬浮提示 -->
                <template #reference>
                  <el-tooltip content="选择基团范围" placement="top">
                    <el-button
                      type="primary"
                      icon="el-icon-s-operation"
                      size="mini"
                      circle
                      style="
                        position: absolute;
                        top: 5px;
                        left: 5px;
                        z-index: 20;
                      "
                    />
                  </el-tooltip>
                </template>
              </el-popover>

              <!-- 波长图表 -->
              <wavelength-selection-chart
                ref="wavelengthChart"
                v-if="chooseWavelength.length > 0"
                :fullSpectrumData="seriesData2"
                :fullXAxisData="xAxisData2"
                :selectedWavelengths="chooseWavelength"
                :sampleIndex="0"
              />

              <!-- 空状态 -->
              <div v-else class="empty-chart-placeholder">
                <div class="chart-title">波长选择吸光度曲线</div>
                <div class="placeholder-content">
                  <i class="el-icon-info"></i>
                  <p>请先完成波长选择步骤</p>
                </div>
              </div>
            </div>
          </el-col>
        </el-row>
      </el-card>

      <el-card shadow="always" style="padding: 20px; margin-top: 10px">
        <el-row :gutter="20" style="margin-top: 20px">
          <el-col :span="8">
            <scree-plot-chart
              ref="screeChart"
              v-if="screeX.length > 0"
              title="碎石图（Scree plot）"
              :screeX="screeX"
              :screeBar="screeBar"
              :screePlot="screePlot"
            />
            <div v-else class="empty-chart-placeholder">
              <div class="chart-title">碎石图（Scree plot）</div>
              <div class="placeholder-content">
                <i class="el-icon-info"></i>
                <p>请先完成主成分分析步骤</p>
              </div>
            </div>
          </el-col>
          <el-col :span="8">
            <stacked-line-chart
              ref="loadingChart"
              v-if="seriesData5.length > 0"
              title="负载图（Loading plot）"
              :xAxisData="xAxisData5"
              :seriesData="seriesData5"
              :showLegend="true"
            />
            <div v-else class="empty-chart-placeholder">
              <div class="chart-title">负载图（Loading plot）</div>
              <div class="placeholder-content">
                <i class="el-icon-info"></i>
                <p>请先完成主成分分析步骤</p>
              </div>
            </div>
          </el-col>
          <el-col :span="8">
            <pca-scatter-chart
              ref="scoreChart"
              v-if="scoreScatterPc1.length > 0"
              title="得分图（Score plot）"
              :scoreScatterPc1="scoreScatterPc1"
              :scoreScatterPc2="scoreScatterPc2"
              :pc1Value="pc1Value"
              :pc2Value="pc2Value"
              :ellipseAngle="ellipseAngle"
              :ellipseWidth="ellipseWidth"
              :ellipseHeight="ellipseHeight"
            />
            <div v-else class="empty-chart-placeholder">
              <div class="chart-title">得分图（Score plot）</div>
              <div class="placeholder-content">
                <i class="el-icon-info"></i>
                <p>请先完成主成分分析步骤</p>
              </div>
            </div>
          </el-col>
        </el-row>
      </el-card>

      <el-card shadow="always" style="padding: 2px; margin-top: 10px">
        <el-row :gutter="20" style="margin-top: 20px" height="300px">
          <el-col :span="16">
            <el-table
              ref="modelDetailTable"
              :data="modelDetailtableData"
              border
              style="width: 100%"
              max-height="300"
              :header-cell-style="{ textAlign: 'center' }"
              :cell-style="{ textAlign: 'center' }"
              @selection-change="handleModelDetailSelection"
              highlight-current-row
            >
              <el-table-column
                type="selection"
                width="55"
                align="center"
              ></el-table-column>
              <!-- <el-table-column
              prop="modelNo" 
              label="模型详情编号"
              :show-overflow-tooltip="true"
            ></el-table-column> -->
              <el-table-column
                prop="latentVariableCount"
                label="潜在变量数"
                align="center"
              >
              </el-table-column>
              <el-table-column prop="rc2" label="Rc²"></el-table-column>
              <el-table-column prop="rmsecv" label="RMSECV"></el-table-column>
              <el-table-column prop="rp2" label="Rp²"></el-table-column>
              <el-table-column prop="rmsep" label="RMSEP"></el-table-column>
              <el-table-column prop="rpd" label="RPD"></el-table-column>
              <el-table-column
                prop="t_value"
                :label="tTestColumnLabel"
              ></el-table-column>
              <el-table-column label="操作" align="center" width="120">
                <template slot-scope="scope">
                  <span
                    class="correlation-link"
                    @click="showCorrelationChart(scope.row, scope.$index + 1)"
                  >
                    相关性图
                  </span>
                </template>
              </el-table-column>
            </el-table>
          </el-col>
          <el-col :span="8">
            <model-evaluation-chart
              ref="modelEvalChart"
              title="模型评价曲线"
              :modelDetailData="modelDetailtableData"
            />
          </el-col>
        </el-row>
      </el-card>

      <el-dialog
        title="导入校正集"
        showReverseWarning
        ref="modelDataDialog"
        :visible.sync="calibrationdialogVisible"
        width="80vw"
        append-to-body
      >
        <model-data
          ref="constructTable"
          :is-row-visible="false"
          :get-finished-data="true"
          @selection-change="handleCalibrationSelection"
          style="height: 100%; overflow-y: scroll"
        />
        <div slot="footer" class="dialog-footer">
          <el-button type="primary" @click="getCalibration">确 认</el-button>
          <el-button @click="calibrationdialogVisible = false">取 消</el-button>
        </div>
      </el-dialog>

      <!-- 预处理方法选择框 -->
      <el-dialog
        title="选择预处理方法"
        :visible.sync="preProcessdialogVisible"
        width="80vw"
        append-to-body
      >
        <div class="preprocess-method-container">
          <!-- 启用方法区域 -->
          <div class="enabled-methods-section">
            <h4>已启用方法（可拖动排序）</h4>
            <draggable
              v-model="enabledMethods"
              class="enabled-methods-list"
              handle=".drag-handle"
              ghost-class="drag-ghost"
              chosen-class="drag-chosen"
              @start="onDragStart"
              @end="onDragEnd"
            >
              <div
                v-for="(method, index) in enabledMethods"
                :key="method.code"
                class="method-item enabled"
              >
                <div class="method-drag-handle">
                  <i class="el-icon-rank drag-handle"></i>
                  <span class="method-order">{{ index + 1 }}</span>
                </div>

                <div class="method-content">
                  <div class="method-header">
                    <span class="method-name">{{ method.name }}</span>
                    <el-button
                      type="danger"
                      icon="el-icon-close"
                      circle
                      size="mini"
                      @click="disableMethod(method.code)"
                    />
                  </div>

                  <div v-if="needsParam(method.code)" class="method-params">
                    <el-input
                      v-model="method.param1"
                      size="small"
                      placeholder="窗口大小"
                      style="margin-right: 8px; width: 120px"
                    />
                    <el-input
                      v-model="method.param2"
                      size="small"
                      placeholder="阶数"
                      style="width: 120px"
                    />
                  </div>
                </div>
              </div>
            </draggable>

            <div v-if="enabledMethods.length === 0" class="empty-tip">
              <i class="el-icon-info"></i>
              <span>暂无启用方法，请从下方选择</span>
            </div>
          </div>

          <!-- 未启用方法区域 -->
          <div class="available-methods-section">
            <h4>可用方法</h4>
            <div class="available-methods-grid">
              <div
                v-for="method in disabledMethods"
                :key="method.code"
                class="method-item disabled"
                @click="enableMethod(method.code)"
              >
                <div class="method-info">
                  <span class="method-name">{{ method.name }}</span>
                  <span class="method-type-tag" :class="method.type">
                    {{ getTypeLabel(method.type) }}
                  </span>
                </div>
                <el-button
                  type="primary"
                  icon="el-icon-plus"
                  circle
                  size="mini"
                />
              </div>
            </div>
          </div>
        </div>

        <div slot="footer" class="dialog-footer">
          <div class="footer-actions">
            <el-button @click="resetMethods">重置</el-button>
            <el-button @click="preProcessdialogVisible = false">取消</el-button>
            <el-button type="primary" @click="confirmPreprocessing"
              >确认</el-button
            >
          </div>
        </div>
      </el-dialog>

      <!-- 波长选择方法设置框 -->
      <el-dialog
        title="波长选择方法设置"
        :visible.sync="wavelengthChooseVisible"
        width="80vw"
        append-to-body
      >
        <el-table :data="wavelengthChooseList" border style="width: 100%">
          <!-- 启用 -->
          <el-table-column label="启用" width="60" align="center">
            <template slot-scope="scope">
              <el-checkbox
                :value="scope.row.enabled"
                @change="handleSingleSelect(scope.$index)"
              />
            </template>
          </el-table-column>

          <!-- 名称 -->
          <el-table-column prop="name" label="名称" align="center" />

          <!-- 设置参数1 -->
          <el-table-column label="设置参数1" width="120" align="center">
            <template slot-scope="scope">
              <el-input
                v-if="scope.row.paramLabels[0]"
                v-model="scope.row.params[0]"
                :placeholder="scope.row.paramLabels[0]"
                size="mini"
              />
            </template>
          </el-table-column>

          <!-- 设置参数2 -->
          <el-table-column label="设置参数2" width="120" align="center">
            <template slot-scope="scope">
              <el-input
                v-if="scope.row.paramLabels[1]"
                v-model="scope.row.params[1]"
                :placeholder="scope.row.paramLabels[1]"
                size="mini"
              />
            </template>
          </el-table-column>

          <!-- 设置参数3 -->
          <el-table-column label="设置参数3" width="120" align="center">
            <template slot-scope="scope">
              <el-input
                v-if="scope.row.paramLabels[2]"
                v-model="scope.row.params[2]"
                :placeholder="scope.row.paramLabels[2]"
                size="mini"
              />
            </template>
          </el-table-column>
        </el-table>

        <div slot="footer" class="dialog-footer">
          <el-button type="primary" @click="handleWavelengthSelection"
            >确 认</el-button
          >
          <el-button @click="wavelengthChooseVisible = false">取 消</el-button>
        </div>
      </el-dialog>

      <!-- 训练参数设置对话框 -->
      <el-dialog
        title="训练参数设置"
        :visible.sync="trainingParamsVisible"
        width="500px"
        append-to-body
      >
        <el-form :model="trainingParams" label-width="180px">
          <el-form-item label="交叉验证比例（训练集/测试集）：">
            <el-select
              v-model="trainingParams.crossValidationRatio"
              placeholder="请选择"
              style="width: 100%"
            >
              <el-option label="2:1" value="0.3"></el-option>
              <el-option label="3:1" value="0.4"></el-option>
              <el-option label="4:1" value="0.5"></el-option>
            </el-select>
          </el-form-item>

          <el-form-item label="最大潜在变量数：">
            <div style="display: flex; align-items: center; gap: 10px">
              <el-button
                size="mini"
                @click="decreaseLatentVariables"
                :disabled="trainingParams.maxLatentVariables <= 1"
              >
                -
              </el-button>
              <el-input-number
                v-model="trainingParams.maxLatentVariables"
                :min="1"
                :max="20"
                controls-position="right"
                style="width: 80px"
              />
              <el-button
                size="mini"
                @click="increaseLatentVariables"
                :disabled="trainingParams.maxLatentVariables >= 20"
              >
                +
              </el-button>
            </div>
          </el-form-item>
        </el-form>

        <div slot="footer" class="dialog-footer">
          <el-button @click="trainingParamsVisible = false">取消</el-button>
          <el-button type="primary" @click="confirmTraining">确认</el-button>
        </div>
      </el-dialog>

      <!-- 相关性图对话框 -->
      <el-dialog
        title="相关性图信息"
        :visible.sync="correlationDialogVisible"
        width="80%"
        append-to-body
        @open="onCorrelationDialogOpen"
      >
        <div v-if="currentRowData">
          <!-- 相关性散点图 -->
          <div class="correlation-chart-container">
            <correlation-chart
              ref="correlationChart"
              :row-data="currentRowData"
              :latent-variable-count="currentLatentVariableCount"
              width="100%"
            />
          </div>
        </div>

        <div slot="footer" class="dialog-footer">
          <el-button @click="correlationDialogVisible = false">关闭</el-button>
        </div>
      </el-dialog>
    </div>
  </div>
</template>

<script>
import StackedLineChart from "@/views/tool/build/ChartComponent.vue"; // 路径根据项目调整
import ModelData from "@/views/model/construct/index.vue";
import PcaScatterChart from "@/components/PcaScatterChart.vue";
import ScreePlotChart from "@/components/ScreePlotChart.vue";
import WavelengthSelectionChart from "@/components/WavelengthSelectionChart.vue";
import ModelEvaluationChart from "@/components/ModelEvaluationChart.vue";
import CorrelationChart from "@/components/CorrelationChart.vue"; // 新增导入
import * as echarts from "echarts";
import {
  saveModelData,
  delModelDetailData,
  getModelDetailData,
  updateData as updateModelData,
} from "@/api/model/data";
import { addModelDetail } from "@/api/model/modelDetail";
import { addPreprocessing } from "@/api/model/preprocessing";
import { addWaveSelect } from "@/api/model/waveSelect";
import { listConstructDetail } from "@/api/model/constructDetail";
import { listDetail } from "@/api/constituent/detail";
import draggable from "vuedraggable";
import axios from "axios";
import { downloadPDF } from "@/utils/htmlToPdf.js";
import msyhFont from "@/assets/fonts/msyh-normal.js"; // base64字体

export default {
  components: {
    StackedLineChart,
    ModelData,
    draggable,
    PcaScatterChart,
    ScreePlotChart,
    WavelengthSelectionChart,
    ModelEvaluationChart,
    CorrelationChart // 新增组件注册
  },
  data() {
    return {
      reportData: {
        modelBaseInfo: {
          modelName: "", // 模型名称
          modelNo: "", // 模型编号
          constituentName: "", // 分析成分名称
          predict_max: "", // 预测值最大值
          predict_min: "", // 预测值最小值
          // 后台获取
          creator: "", // 建立人
          createTime: "", // 建立时间
        },
        modelConstructInfo: {
          sampleProperty: "", // 样品属性
          totalSampleCount: 0, // 样品总数量
          samplePreparationMethod: "", // 样品制备方法
          spectralSamplingInstrument: "", // 光谱采样仪器
          spectralSamplingMethod: "", // 光谱采样方法
          spectralSamplingTime: "", // 光谱采样时间
          standardValueInstrument: "", // 标准值测定仪器
          standardValueMethod: "", // 标准值测定方法
          standardValue_max: "", // 标准值范围
          standardValue_mix: "",
        },
        backgroundSpectrum: {
          spectralSamplingInstrument: "", // 光谱采样仪器
          spectralSamplingTime: "", // 光谱采样时间
        },
        outlierRemoval: {
          methodName: "", // 方法名称及其参数设置
          outlierSamples: [], // 异常样本列表
        },
        preprocessing: {
          methodNameAndParams: "", // 方法名称及其参数设置
        },
        wavelengthSelection: {
          methodNameAndParams: "", // 方法名称及其参数设置
        },
        trainingMethod: {
          crossValidationRatio: "", // 交叉验证集比例
          maxLatentVariables: 0, // 最大潜在变量数
        },
        modelEvaluation: {
          latentVariables: 0, // 模型潜在变量数
          rcSquared: 0, // Rc^2
          rmsecv: 0, // RMSECV
          rpSquared: 0, // Rp^2
          rmsep: 0, // RMSEP
          rpd: 0, // RPD
          tTest: "", // T检验
          boundaryValue: "", // 边界值
        },
      },
      groupInformationList: [], // 存储成分相关联的基团信息
      selectedGroups: [], // 存储用户选中的基团信息
      algorithmSelectedWavelengths: [], // 存储算法选择的波长
      groupSelectedWavelengths: [], // 存储基团选择的波长

      // 模型详情
      modelDetail_data: {},
      // 预处理过程信息
      preProcessing_data: {},
      // 波长选择信息
      waveSelect_data: {},
      modelData: null,
      trainPayload: {
        // 完成预处理后的吸光度曲线
        selected_x: null,

        // 训练集的标准值
        train_y: [],

        // 验证集的原始吸光度曲线
        test_x: [],

        // 验证集的标准值
        test_y: [],

        // 验证集的预处理方法及过程参数
        // 与预处理过程的区别：部分方法需要之前预处理得到的过程参数（可能有或无）
        // 除过程参数外，与原preprocessing_sequence结构一致
        vail_pre_sequence: [], // 修正：应该是数组，包含多个预处理方法对象

        // 波长选择的索引
        selected_indices: [],

        // 最大潜在变量数（最大主成分数）
        max_components: 0,

        // 交叉验证折数
        // 转换规则：2:1→3折、3:1→4折、4:1→5折
        cv_folds: 0,
      },
      train_result: null,
      critical_t: null, // 存储临界t值
      currentStep: 0, // 当前步骤索引
      standardValueList: [], // 标准值存储列表
      preProcessing_result: null, // 预处理结果
      selected_indices: null, // 存储选择的波长索引
      startWavelength: 0, // 起始波长
      endWavelength: 0,
      mask: null, // 掩码
      modelNo: "",
      modelId: "", // 模型ID
      pca_req: null,
      // PCA相关数据
      scoreScatterPc1: [],
      scoreScatterPc2: [],
      pc1Value: 0,
      pc2Value: 0,
      // 椭圆参数
      ellipseAngle: 0,
      ellipseWidth: 0,
      ellipseHeight: 0,
      // 碎石图数据
      screeX: [],
      screeBar: [],
      screePlot: [],
      modelDetailtableData: [],
      selectedModelDetail: null, // 存储选中的模型详情数据
      dragStartIndex: null,
      preProcessing_waveData: [],
      selectedConstructData: null, // 用于存储选中的建模数据行
      chooseWavelength: [], // 存储选中的波长点
      calibrationdialogVisible: false,
      preProcessdialogVisible: false,
      wavelengthChooseVisible: false,
      trainingParamsVisible: false,
      trainingParams: {
        crossValidationRatio: "0.3", // 默认2:1
        maxLatentVariables: 5, // 默认5个潜在变量
      },
      // 相关性图对话框相关变量
      correlationDialogVisible: false,
      currentRowData: null,
      currentLatentVariableCount: 0,
      xAxisData1: [],
      xAxisData2: [],
      xAxisData3: [],
      xAxisData4: [],
      xAxisData5: [],
      xAxisData6: [],
      seriesData1: [],
      seriesData2: [],
      seriesData3: [],
      seriesData4: [],
      seriesData5: [],
      seriesData6: [],
      preProcessingPayload: {
        // 预处理方法序列
        preprocessing_sequence: [],
        // 预处理波长数据
        wave_data: [],
      },
      methodList: [
        {
          code: "01",
          type: "baseline",
          name: "差分一阶导",
          e_Name: "diff_derivative",
          enabled: false,
        },
        {
          code: "02",
          type: "baseline",
          name: "差分二阶导",
          e_Name: "diff_derivative",
          enabled: false,
        },
        {
          code: "03",
          type: "baseline",
          name: "SG一阶导",
          e_Name: "sg_derivative",
          enabled: false,
          param1: "",
          param2: "",
        },
        {
          code: "04",
          type: "baseline",
          name: "SG二阶导",
          e_Name: "sg_derivative",
          enabled: false,
          param1: "",
          param2: "",
        },
        {
          code: "05",
          type: "scatter",
          name: "标准正交变量（SNV）",
          e_Name: "snv",
          enabled: false,
        },
        {
          code: "06",
          type: "scatter",
          name: "多元散射校正（MSC）",
          e_Name: "msc",
          enabled: false,
        },
        {
          code: "07",
          name: "SG平滑处理",
          e_Name: "sg_smoothing",
          type: "smoothing",
          enabled: false,
          param1: "",
          param2: "",
        },
        {
          code: "08",
          type: "scaling",
          name: "均值中心化",
          e_Name: "centering",
          enabled: false,
        },
        {
          code: "09",
          type: "scaling",
          name: "归一化",
          e_Name: "normalization",
          enabled: false,
        },
        {
          code: "10",
          type: "scaling",
          name: "标准化",
          e_Name: "standardization",
          enabled: false,
        },
      ],
      wavelengthChooseList: [
        {
          code: "01",
          name: "无信息变量消除（VUE）",
          method: "VUE",
          enabled: false,
          params: ["", "", ""],
          paramLabels: ["特征数", "迭代数", ""],
        },
        {
          code: "02",
          name: "相关系数法",
          method: "CORRELATION",
          enabled: false,
          params: ["", "", ""],
          paramLabels: ["阈值", "", ""],
        },
        {
          code: "03",
          name: "连续投影算法（SPA）",
          enabled: false,
          params: ["", "", ""],
          paramLabels: ["特征数", "", ""],
        },
        {
          code: "04",
          name: "竞争性自适应加权采样（CARS）",
          method: "CARS",
          enabled: false,
          params: ["", "", ""],
          paramLabels: ["特征数", "迭代数", ""],
        },
        {
          code: "05",
          name: "变量投影重要性（VIP）",
          method: "VIPSelection",
          enabled: false,
          params: ["", "", ""],
          paramLabels: ["特征数", "", ""],
        },
        {
          code: "06",
          name: "区间组合偏最小二乘法（siPLS）",
          method: "SiPLS",
          enabled: false,
          params: ["3", "2", "2"],
          paramLabels: ["迭代数", "变量数", "成分数"],
        },
      ],
      originalMethods: [], // 备份原始方法列表
    };
  },
  computed: {
    // sortedMethodList() {
    //   return [...this.methodList].sort((a, b) => {
    //     // enabled true 的排前面
    //     if (a.enabled === b.enabled) return 0;
    //     return a.enabled ? -1 : 1;
    //   });
    // },

    // 动态生成t检验列头标签
    tTestColumnLabel() {
      if (this.critical_t !== null) {
        return `t检验（${this.critical_t}）`;
      }
      return "t检验";
    },
    enabledMethods: {
      get() {
        return this.methodList.filter((item) => item.enabled);
      },
      set(value) {
        // 创建一个新数组来保存更新后的methodList
        const updatedMethodList = [];

        // 首先添加拖拽排序后的启用方法
        value.forEach((enabledMethod) => {
          const originalMethod = this.methodList.find(
            (item) => item.code === enabledMethod.code
          );
          if (originalMethod) {
            updatedMethodList.push(originalMethod);
          }
        });

        // 然后添加未启用的方法
        this.methodList.forEach((method) => {
          if (!method.enabled) {
            updatedMethodList.push(method);
          }
        });

        // 更新methodList
        this.methodList = updatedMethodList;
      },
    },
    disabledMethods() {
      return this.methodList.filter((item) => !item.enabled);
    },
  },
  watch: {
    // 监听对话框显示状态
    // 移除了 correlationDialogVisible 的监听
    correlationDialogVisible: {
      handler(newVal) {
        if (newVal) {
          // 对话框打开时，确保图表更新
          this.$nextTick(() => {
            if (this.$refs.correlationChart) {
              this.$refs.correlationChart.updateChart();
            }
          });
        }
      }
    }
  },
  mounted() {
    window.addEventListener("beforeunload", this.handleBeforeUnload);
    this.modelNo = this.$route.query.modelNo || "";
    this.modelId = this.$route.query.modelId || "";
    console.log("接收到的参数：", this.modelNo);
    console.log("接收到的参数：", this.modelId);
    // 初始化时绑定启用变化监听
    this.watchMethodChange();
  },

  beforeDestroy() {
    window.removeEventListener("beforeunload", this.handleBeforeUnload);
    // 清理相关性图表实例
    // this.destroyCorrelationChart(); // 已移至CorrelationChart组件内部处理
  },
  // beforeRouteLeave(to, from, next) {
  //   if (true) {
  //     // 必须在 beforeRouteLeave 中直接调用 next，否则会被自动放行
  //     this.$confirm("表单数据尚未保存，是否关闭页面？", "提示", {
  //       confirmButtonText: "确认",
  //       cancelButtonText: "取消",
  //       type: "warning",
  //     })
  //       .then(() => {
  //         next(); // 用户点击确认，放行
  //       })
  //       .catch(() => {
  //         next(false); // 用户点击取消，阻止跳转/关闭
  //       });
  //   } else {
  //     next(); // 无需提示，直接放行
  //   }
  // },

  methods: {
    // 根据选中的基团更新波长选择
    updateWavelengthSelection() {
      if (!this.xAxisData2 || this.xAxisData2.length === 0) {
        this.$message.warning("请先完成数据预处理步骤");
        return;
      }

      // 重新计算基团选择的波长
      this.recalculateGroupWavelengths();

      // 合并算法和基团选择的波长
      this.mergeWavelengthSelections();
    },

    // 重新计算基团选择的波长
    recalculateGroupWavelengths() {
      const allWavelengths = this.xAxisData2.map(Number);
      this.groupSelectedWavelengths = [];

      this.selectedGroups.forEach((groupId) => {
        const group = this.groupInformationList.find((g) => g.id === groupId);
        if (group) {
          // 检查基团波长范围是否在当前波长范围内
          const minWave = Math.max(this.startWavelength, group.minWavelength);
          const maxWave = Math.min(this.endWavelength, group.maxWavelength);

          if (minWave > maxWave) {
            this.$message.warning(
              `基团 ${group.name} 的波长范围(${group.minWavelength}-${group.maxWavelength})不在当前光谱范围内`
            );
            return;
          }

          // 将基团波长范围内的点添加到选择中
          allWavelengths.forEach((wavelength) => {
            if (wavelength >= minWave && wavelength <= maxWave) {
              if (!this.groupSelectedWavelengths.includes(wavelength)) {
                this.groupSelectedWavelengths.push(wavelength);
              }
            }
          });
        }
      });

      console.log("基团选择波长更新:", {
        选中基团数: this.selectedGroups.length,
        基团波长数: this.groupSelectedWavelengths.length,
      });
    },

    // 合并算法和基团选择的波长
    mergeWavelengthSelections() {
      // 合并算法选择和基团选择的波长（去重）
      const algorithmWavelengths =
        this.algorithmSelectedWavelengths.map(Number);
      const combinedWavelengths = [
        ...new Set([...algorithmWavelengths, ...this.groupSelectedWavelengths]),
      ];

      // 更新选中的波长
      this.chooseWavelength = combinedWavelengths
        .map(String)
        .sort((a, b) => Number(a) - Number(b));

      // 更新mask和selected_indices
      this.updateMaskAndIndices();

      console.log("波长选择合并更新:", {
        算法选择波长数量: algorithmWavelengths.length,
        基团选择波长数量: this.groupSelectedWavelengths.length,
        合并后波长数量: this.chooseWavelength.length,
        selectedIndices长度: this.selected_indices
          ? this.selected_indices.length
          : 0,
      });

      // 通知组件重绘
      this.$nextTick(() => {
        // 触发波长选择图表重绘
        this.$forceUpdate();
      });
    },
    // 更新mask和selected_indices
    updateMaskAndIndices() {
      if (!this.xAxisData2 || this.xAxisData2.length === 0) {
        return;
      }

      // 更新mask
      this.mask = this.xAxisData2.map((wavelength) =>
        this.chooseWavelength.includes(wavelength)
      );

      // 更新selected_indices
      this.selected_indices = [];
      this.xAxisData2.forEach((wavelength, index) => {
        if (this.chooseWavelength.includes(wavelength)) {
          this.selected_indices.push(index);
        }
      });

      console.log("更新mask和selected_indices:", {
        maskLength: this.mask.length,
        selectedIndicesLength: this.selected_indices.length,
        selectedWavelengthsCount: this.chooseWavelength.length,
      });
    },
    // 处理基团选择变化
    handleGroupSelectionChange(selectedGroups) {
      console.log("基团选择变化:", {
        之前: this.selectedGroups,
        现在: selectedGroups,
      });

      this.selectedGroups = selectedGroups;

      // 无论选中还是取消，都重新计算波长选择
      this.updateWavelengthSelection();
    },
    // 获取成分关联的基团信息
    async fetchGroupInformation() {
      try {
        if (
          !this.selectedConstructData ||
          !this.selectedConstructData.constituentId
        ) {
          console.warn("没有选中的校正集数据或成分ID，无法获取基团信息");
          return;
        }

        console.log(
          "正在获取成分关联基团信息，成分ID:",
          this.selectedConstructData.constituentId
        );

        // 调用成分明细接口，根据成分ID查询关联的基团信息
        const response = await listDetail({
          constituentId: this.selectedConstructData.constituentId,
          pageNum: 1,
          pageSize: 9999, // 获取所有关联基团
        });

        if (response && response.rows) {
          const detailList = response.rows || [];

          // 转换数据格式，将成分明细转换为基团信息格式
          this.groupInformationList = detailList
            .map((detail) => ({
              id: detail.groupId || detail.id,
              name: detail.groupName,
              groupSymbol: detail.groupSymbol,
              vibrationType: detail.vibrationType,
              minWavelength: detail.startWavelength,
              maxWavelength: detail.endWavelength,
              startWavenumber: detail.startWavenumber,
              endWavenumber: detail.endWavenumber,
              remarks: detail.remarks,
            }))
            .filter(
              (group) =>
                group.name && group.minWavelength && group.maxWavelength
            );

          console.log("成功获取基团信息:", this.groupInformationList);

          if (this.groupInformationList.length === 0) {
            this.$message.info("该成分暂无关联的基团信息");
          } else {
            this.$message.success(
              `成功加载${this.groupInformationList.length}个关联基团`
            );
          }
        } else {
          console.error("获取基团信息失败:", response);
          this.$message.error("获取基团信息失败");
        }
      } catch (error) {
        console.error("获取基团信息失败:", error);
        this.$message.error(
          "获取基团信息失败: " + (error.message || "网络错误")
        );

        // 如果API调用失败，使用模拟数据作为fallback（仅供调试）
        console.log("使用模拟数据作为备选方案");
        this.groupInformationList = [
          { id: 1, name: "羟基", minWavelength: 300, maxWavelength: 400 },
          { id: 2, name: "羧基", minWavelength: 450, maxWavelength: 550 },
          { id: 3, name: "酮基", minWavelength: 950, maxWavelength: 1080 },
        ];
      }
    },
    handleBeforeUnload(event) {
      // 自定义提示内容，部分浏览器会忽略这段内容，但仍会弹出默认提示框
      event.preventDefault();
      event.returnValue = "您确定要离开此页面吗？所有未保存的数据将会丢失。";
    },
    closePage() {
      this.$confirm(
        "您确定要离开此页面吗？所有未保存的数据将会丢失。",
        "提示",
        {
          confirmButtonText: "确认",
          cancelButtonText: "取消",
          type: "warning",
        }
      )
        .then(() => {
          const visitedViews = this.$store.state.tagsView.visitedViews;
          const currentPath = this.$route.fullPath;

          const index = visitedViews.findIndex(
            (view) => view.fullPath === currentPath
          );

          // 找到要跳转的下一个页面
          const nextView = visitedViews[index - 1] ||
            visitedViews[index + 1] || { path: "/" };

          // 删除当前页面标签并跳转
          this.$store
            .dispatch("tagsView/delVisitedView", this.$route)
            .then(() => {
              this.$router.push(nextView.path);
            });
        })
        .catch(() => {
          // 用户点击“取消”，不做任何处理
        });
    },
    // 处理模型详情表格选择变化
    handleModelDetailSelection(selection) {
      // 限制只能选择一行
      if (selection.length > 1) {
        const lastSelected = selection[selection.length - 1];
        this.$nextTick(() => {
          this.$refs.modelDetailTable.clearSelection();
          this.$refs.modelDetailTable.toggleRowSelection(lastSelected, true);
        });
        this.selectedModelDetail = lastSelected;
      } else {
        this.selectedModelDetail = selection[0] || null;
      }

      if (this.selectedModelDetail) {
        console.log("选中的模型详情:", this.selectedModelDetail);
      }
    },
    async exportReport() {
      if (!this.selectedModelDetail) {
        this.$message.warning("请先选择要导出的模型详情");
        return;
      }

      try {
        this.$message.info("正在生成PDF报告，请稍候...");

        // 获取所有图表的图片数据
        const chartImages = await this.getChartImages();

        // 填充报告数据
        this.fillReportData();

        // 创建报告HTML内容并生成PDF
        await this.generateReportHTML(chartImages);
      } catch (error) {
        console.error("导出报告失败:", error);
        this.$message.error("导出报告失败: " + error.message);
      }
    },

    // 获取所有图表的图片数据
    async getChartImages() {
      const images = {};
      try {
        // 1. 预处理光谱吸光度曲线
        const preprocessChart = this.$refs.preprocessChart;
        if (
          preprocessChart &&
          typeof preprocessChart.getChartBase64 === "function"
        ) {
          images.preprocessSpectrum = preprocessChart.getChartBase64();
        }
        // 2. 波长选择光谱吸光度曲线
        const wavelengthChart = this.$refs.wavelengthChart;
        if (
          wavelengthChart &&
          typeof wavelengthChart.getChartBase64 === "function"
        ) {
          images.wavelengthSpectrum = wavelengthChart.getChartBase64();
        }
        // 3. 训练模型评价曲线
        const modelEvalChart = this.$refs.modelEvalChart;
        if (
          modelEvalChart &&
          typeof modelEvalChart.getChartBase64 === "function"
        ) {
          images.modelEvaluation = modelEvalChart.getChartBase64();
        } else if (
          modelEvalChart &&
          typeof modelEvalChart.exportChartImage === "function"
        ) {
          // 如果没有getChartBase64方法，尝试获取图表实例并转换为base64
          const chartInstance =
            modelEvalChart.chart || modelEvalChart.$refs?.chart?.chart;
          if (chartInstance) {
            images.modelEvaluation = chartInstance.getDataURL({
              type: "png",
              pixelRatio: 2,
              backgroundColor: "#fff",
            });
          }
        }
        // 4. 碎石图
        const screeChart = this.$refs.screeChart;
        if (screeChart && typeof screeChart.getChartBase64 === "function") {
          images.screePlot = screeChart.getChartBase64();
        }
        // 5. 负载图
        const loadingChart = this.$refs.loadingChart;
        if (loadingChart && typeof loadingChart.getChartBase64 === "function") {
          images.loadingPlot = loadingChart.getChartBase64();
        }
        // 6. 得分图
        const scoreChart = this.$refs.scoreChart;
        if (scoreChart && typeof scoreChart.getChartBase64 === "function") {
          images.scorePlot = scoreChart.getChartBase64();
        } else if (
          scoreChart &&
          typeof scoreChart.exportChartImage === "function"
        ) {
          // 如果没有getChartBase64方法，尝试获取图表实例并转换为base64
          const chartInstance =
            scoreChart.chart || scoreChart.$refs?.chart?.chart;
          if (chartInstance) {
            images.scorePlot = chartInstance.getDataURL({
              type: "png",
              pixelRatio: 2,
              backgroundColor: "#fff",
            });
          }
        }
        return images;
      } catch (error) {
        console.error("获取图表数据失败:", error);
        return {};
      }
    },

    // 填充报告数据
    fillReportData() {
      // 基本模型信息
      this.reportData.modelBaseInfo.modelName =
        this.selectedModelDetail.modelName || "";
      this.reportData.modelBaseInfo.modelNo =
        this.selectedModelDetail.modelNo || this.modelNo || "";
      this.reportData.modelBaseInfo.componentName =
        this.selectedModelDetail.componentName || "";
      this.reportData.modelBaseInfo.predict_max =
        this.selectedModelDetail.predict_max || "";
      this.reportData.modelBaseInfo.predict_min =
        this.selectedModelDetail.predict_min || "";
      this.reportData.modelBaseInfo.creator =
        this.selectedModelDetail.creator || "";
      this.reportData.modelBaseInfo.createTime =
        this.selectedModelDetail.createTime || new Date().toLocaleString();

      // 模型构建信息
      this.reportData.modelConstructInfo.sampleProperty = "金银花";
      this.reportData.modelConstructInfo.totalSampleCount =
        this.totalSampleCount || 50;
      this.reportData.modelConstructInfo.samplePreparationMethod =
        "除去枝叶杂质、粉碎";
      this.reportData.modelConstructInfo.spectralSamplingInstrument =
        "xxx近红外光谱仪";
      this.reportData.modelConstructInfo.spectralSamplingMethod =
        "仪器温度27℃，扫描方式为漫反射，光谱分辨猇8cm^-1";
      this.reportData.modelConstructInfo.spectralSamplingTime =
        "2025-08-11 12:00:00";
      this.reportData.modelConstructInfo.standardValueInstrument =
        "高效液相色谱仪";
      this.reportData.modelConstructInfo.standardValueMethod =
        "测定方法同《中国药典》（一部）酚酸类含量测定";
      this.reportData.modelConstructInfo.standardValueRange = `绿原酸含量为${this.reportData.modelBaseInfo.predict_min}%~${this.reportData.modelBaseInfo.predict_max}%`;

      // 填充其他数据
      this.reportData.backgroundSpectrum.spectralSamplingInstrument =
        "xxx近红外光谱仪";
      this.reportData.backgroundSpectrum.spectralSamplingTime =
        "2025-08-11 11:00:00";

      this.reportData.outlierRemoval.methodName = "PCA结合马氏距离";
      this.reportData.outlierRemoval.outlierSamples = [
        "CON202508110110-9.872",
        "CON202508110112-10.097",
      ];

      this.reportData.preprocessing.methodNameAndParams =
        "均值中心化-SG一阶导（5，3）-SNV";

      this.reportData.wavelengthSelection.methodNameAndParams =
        "区间组合偏最小二乘法SiPLS（3,2,2）";

      this.reportData.trainingMethod.crossValidationRatio = "4：1";
      this.reportData.trainingMethod.maxLatentVariables = 10;

      this.reportData.modelEvaluation.latentVariables =
        this.selectedModelDetail.latentVariableCount || 0;
      this.reportData.modelEvaluation.rcSquared =
        this.selectedModelDetail.rc2 || 0;
      this.reportData.modelEvaluation.rmsecv =
        this.selectedModelDetail.rmsecv || 0;
      this.reportData.modelEvaluation.rpSquared =
        this.selectedModelDetail.rp2 || 0;
      this.reportData.modelEvaluation.rmsep =
        this.selectedModelDetail.rmsep || 0;
      this.reportData.modelEvaluation.rpd = this.selectedModelDetail.rpd || 0;
      this.reportData.modelEvaluation.tTest =
        this.selectedModelDetail.t_value || "";
      this.reportData.modelEvaluation.boundaryValue = "2.1448";
    },

    // 生成PDF
    async generatePDF(chartImages) {
      // 动态导入PDF生成库
      let jsPDF, html2canvas;
      try {
        // 尝试导入jspdf和html2canvas
        const jsPDFModule = await import("jspdf");
        jsPDF = jsPDFModule.default;
        html2canvas = (await import("html2canvas")).default;
      } catch (error) {
        console.warn("PDF生成库未安装，使用HTML格式:", error);
        // 如果库未安装，回退到HTML格式
        return this.generateHTMLFallback(chartImages);
      }

      try {
        // 创建PDF文档 (A4尺寸)
        const pdf = new jsPDF({
          orientation: "portrait",
          unit: "mm",
          format: "a4",
        });
        pdf.setFont("msyh"); // ✅ 设置中文字体
        pdf.setFontSize(18);
        pdf.text("PLS定量模型建立与验证报告", 105, 20, { align: "center" });

        const pageWidth = 210; // A4宽度
        const pageHeight = 297; // A4高度
        const margin = 20; // 页边距
        const contentWidth = pageWidth - 2 * margin;
        let currentY = margin;

        // 添加标题
        pdf.setFontSize(18);
        pdf.setFont("helvetica", "bold");
        pdf.text("PLS定量模型建立与验证报告", pageWidth / 2, currentY, {
          align: "center",
        });
        currentY += 15;

        // 添加模型概要
        this.addSectionToPDF(pdf, "一、模型概要", currentY);
        currentY += 10;

        const modelInfo = [
          ["模型名称", this.reportData.modelBaseInfo.modelName],
          ["模型编号", this.reportData.modelBaseInfo.modelNo],
          ["分析成分名称", this.reportData.modelBaseInfo.componentName],
          [
            "预测值范围",
            `${this.reportData.modelBaseInfo.predict_min}%~${this.reportData.modelBaseInfo.predict_max}%`,
          ],
          ["建立人", this.reportData.modelBaseInfo.creator],
          ["建立时间", this.reportData.modelBaseInfo.createTime],
        ];

        currentY = this.addTableToPDF(
          pdf,
          modelInfo,
          currentY,
          contentWidth,
          margin
        );
        currentY += 10;

        // 添加建模数据信息
        this.addSectionToPDF(pdf, "二、建模数据（校正集、验证集）", currentY);
        currentY += 10;

        const constructInfo = [
          ["样品属性", this.reportData.modelConstructInfo.sampleProperty],
          ["样品总数量", this.reportData.modelConstructInfo.totalSampleCount],
          [
            "样品制备方法",
            this.reportData.modelConstructInfo.samplePreparationMethod,
          ],
          [
            "光谱采样仪器",
            this.reportData.modelConstructInfo.spectralSamplingInstrument,
          ],
          [
            "光谱采样方法",
            this.reportData.modelConstructInfo.spectralSamplingMethod,
          ],
          [
            "光谱采样时间",
            this.reportData.modelConstructInfo.spectralSamplingTime,
          ],
          [
            "标准值测定仪器",
            this.reportData.modelConstructInfo.standardValueInstrument,
          ],
          [
            "标准值测定方法",
            this.reportData.modelConstructInfo.standardValueMethod,
          ],
          ["标准值范围", this.reportData.modelConstructInfo.standardValueRange],
        ];

        currentY = this.addTableToPDF(
          pdf,
          constructInfo,
          currentY,
          contentWidth,
          margin
        );
        currentY += 10;

        // 添加图表（如果有的话）
        if (chartImages.preprocessSpectrum) {
          currentY = await this.addChartToPDF(
            pdf,
            chartImages.preprocessSpectrum,
            "校正集、验证集光谱吸光度曲线",
            currentY,
            contentWidth,
            margin
          );
        }

        // 检查是否需要新页面
        if (currentY > pageHeight - 50) {
          pdf.addPage();
          currentY = margin;
        }

        // 添加其他章节
        this.addSectionToPDF(pdf, "三、背景光谱", currentY);
        currentY += 10;

        const backgroundInfo = [
          [
            "光谱采样仪器",
            this.reportData.backgroundSpectrum.spectralSamplingInstrument,
          ],
          [
            "光谱采样时间",
            this.reportData.backgroundSpectrum.spectralSamplingTime,
          ],
        ];

        currentY = this.addTableToPDF(
          pdf,
          backgroundInfo,
          currentY,
          contentWidth,
          margin
        );
        currentY += 15;

        // 添加数据统计处理工具
        this.addSectionToPDF(pdf, "四、数据统计处理工具", currentY);
        currentY += 10;
        pdf.setFontSize(10);
        pdf.text("Python==3.11.11", margin, currentY);
        currentY += 5;
        pdf.text("numpy==1.26.0", margin, currentY);
        currentY += 5;
        pdf.text("scikit-learn==1.3.0", margin, currentY);
        currentY += 5;
        pdf.text("pandas==2.1.1", margin, currentY);
        currentY += 5;
        pdf.text("scipy==1.11.2", margin, currentY);
        currentY += 15;

        // 添加异常剔除方法
        this.addSectionToPDF(pdf, "五、异常剔除方法", currentY);
        currentY += 10;
        pdf.setFontSize(10);
        pdf.text(
          `方法名称及参数设置：${this.reportData.outlierRemoval.methodName}`,
          margin,
          currentY
        );
        currentY += 5;
        pdf.text(
          `异常样本：${this.reportData.outlierRemoval.outlierSamples.join(
            "、"
          )}`,
          margin,
          currentY
        );
        currentY += 15;

        // 检查是否需要新页面
        if (currentY > pageHeight - 100) {
          pdf.addPage();
          currentY = margin;
        }

        // 添加预处理方法
        this.addSectionToPDF(pdf, "六、预处理方法", currentY);
        currentY += 10;
        pdf.setFontSize(10);
        pdf.text(
          `方法名称及其参数设置：${this.reportData.preprocessing.methodNameAndParams}`,
          margin,
          currentY
        );
        currentY += 10;

        if (chartImages.preprocessSpectrum) {
          currentY = await this.addChartToPDF(
            pdf,
            chartImages.preprocessSpectrum,
            "完成预处理光谱吸光度曲线",
            currentY,
            contentWidth,
            margin
          );
        }

        // 添加波长选择方法
        this.addSectionToPDF(pdf, "七、波长选择方法", currentY);
        currentY += 10;
        pdf.setFontSize(10);
        pdf.text(
          `方法名称及其参数设置：${this.reportData.wavelengthSelection.methodNameAndParams}`,
          margin,
          currentY
        );
        currentY += 10;

        if (chartImages.wavelengthSpectrum) {
          currentY = await this.addChartToPDF(
            pdf,
            chartImages.wavelengthSpectrum,
            "完成波长选择光谱吸光度曲线",
            currentY,
            contentWidth,
            margin
          );
        }

        // 添加训练模型方法
        this.addSectionToPDF(pdf, "八、训练模型方法", currentY);
        currentY += 10;
        pdf.setFontSize(10);
        pdf.text(
          `交叉验证集比例（训练集/测试集）：${this.reportData.trainingMethod.crossValidationRatio}`,
          margin,
          currentY
        );
        currentY += 5;
        pdf.text(
          `最大潜在变量数：${this.reportData.trainingMethod.maxLatentVariables}`,
          margin,
          currentY
        );
        currentY += 10;

        if (chartImages.modelEvaluation) {
          currentY = await this.addChartToPDF(
            pdf,
            chartImages.modelEvaluation,
            "训练模型评价曲线（横轴为潜在变量数）",
            currentY,
            contentWidth,
            margin
          );
        }

        // 检查是否需要新页面
        if (currentY > pageHeight - 100) {
          pdf.addPage();
          currentY = margin;
        }

        // 添加模型评价指标
        this.addSectionToPDF(pdf, "九、本模型评价指标", currentY);
        currentY += 10;

        const evaluationInfo = [
          ["模型潜在变量数", this.reportData.modelEvaluation.latentVariables],
          ["Rc²", this.reportData.modelEvaluation.rcSquared],
          ["RMSECV", this.reportData.modelEvaluation.rmsecv],
          ["Rp²", this.reportData.modelEvaluation.rpSquared],
          ["RMSEP", this.reportData.modelEvaluation.rmsep],
          ["RPD", this.reportData.modelEvaluation.rpd],
          [
            "T检验（边界值）",
            `${this.reportData.modelEvaluation.tTest}（${this.reportData.modelEvaluation.boundaryValue}）`,
          ],
        ];

        currentY = this.addTableToPDF(
          pdf,
          evaluationInfo,
          currentY,
          contentWidth,
          margin
        );
        currentY += 15;

        // 添加相关性曲线
        this.addSectionToPDF(pdf, "十、本模型相关性曲线", currentY);
        currentY += 10;

        // 添加碎石图
        if (chartImages.screePlot) {
          currentY = await this.addChartToPDF(
            pdf,
            chartImages.screePlot,
            "碎石图",
            currentY,
            contentWidth,
            margin
          );
        }

        // 添加负载图
        if (chartImages.loadingPlot) {
          currentY = await this.addChartToPDF(
            pdf,
            chartImages.loadingPlot,
            "负载图",
            currentY,
            contentWidth,
            margin
          );
        }

        // 添加得分图
        if (chartImages.scorePlot) {
          currentY = await this.addChartToPDF(
            pdf,
            chartImages.scorePlot,
            "得分图",
            currentY,
            contentWidth,
            margin
          );
        }

        // 保存PDF
        const pdfBlob = pdf.output("blob");
        const url = URL.createObjectURL(pdfBlob);
        const link = document.createElement("a");
        link.href = url;
        link.download = `建模报告_${this.reportData.modelBaseInfo.modelNo}.pdf`;
        link.click();
        URL.revokeObjectURL(url);

        this.$message.success("PDF报告已生成并下载");

        // 预览选项
        this.$confirm("PDF报告已生成，是否现在预览?", "提示", {
          confirmButtonText: "预览",
          cancelButtonText: "关闭",
          type: "success",
        })
          .then(() => {
            const previewUrl = URL.createObjectURL(pdfBlob);
            window.open(previewUrl, "_blank");
          })
          .catch(() => {});
      } catch (error) {
        console.error("生成PDF失败:", error);
        this.$message.error("生成PDF失败，尝试HTML格式: " + error.message);
        // 回退到HTML格式
        return this.generateHTMLFallback(chartImages);
      }
    },

    // 使用downloadPDF工具生成报告
    async generateReportHTML(chartImages) {
      try {
        // 创建临时的HTML容器
        const reportContainer = document.createElement("div");
        reportContainer.style.cssText = `
          width: 210mm;
          padding: 20mm;
          font-family: 'Microsoft YaHei', Arial, sans-serif;
          font-size: 12px;
          line-height: 1.6;
          color: #333;
          background: white;
          position: absolute;
          left: -9999px;
          top: 0;
          box-sizing: border-box;
          -webkit-print-color-adjust: exact;
          print-color-adjust: exact;
        `;

        // 构建报告HTML内容
        reportContainer.innerHTML = `
          <div style="text-align: center; margin-bottom: 30px;">
            <h1 style="font-size: 24px; margin: 0; color: #2c3e50;">PLS定量模型建立与验证报告</h1>
          </div>

          <div style="margin-bottom: 25px; page-break-inside: avoid;">
            <h2 style="font-size: 16px; color: #34495e; border-bottom: 2px solid #3498db; padding-bottom: 5px;">一、模型概要</h2>
            <table style="width: 100%; border-collapse: collapse; margin-top: 10px;">
              <tr><td style="border: 1px solid #ddd; padding: 8px; width: 30%; background: #f8f9fa;">模型名称</td><td style="border: 1px solid #ddd; padding: 8px;">${
                this.reportData.modelBaseInfo.modelName
              }</td></tr>
              <tr><td style="border: 1px solid #ddd; padding: 8px; background: #f8f9fa;">模型编号</td><td style="border: 1px solid #ddd; padding: 8px;">${
                this.reportData.modelBaseInfo.modelNo
              }</td></tr>
              <tr><td style="border: 1px solid #ddd; padding: 8px; background: #f8f9fa;">分析成分名称</td><td style="border: 1px solid #ddd; padding: 8px;">${
                this.reportData.modelBaseInfo.componentName
              }</td></tr>
              <tr><td style="border: 1px solid #ddd; padding: 8px; background: #f8f9fa;">预测值范围</td><td style="border: 1px solid #ddd; padding: 8px;">${
                this.reportData.modelBaseInfo.predict_min
              }%~${this.reportData.modelBaseInfo.predict_max}%</td></tr>
              <tr><td style="border: 1px solid #ddd; padding: 8px; background: #f8f9fa;">建立人</td><td style="border: 1px solid #ddd; padding: 8px;">${
                this.reportData.modelBaseInfo.creator
              }</td></tr>
              <tr><td style="border: 1px solid #ddd; padding: 8px; background: #f8f9fa;">建立时间</td><td style="border: 1px solid #ddd; padding: 8px;">${
                this.reportData.modelBaseInfo.createTime
              }</td></tr>
            </table>
          </div>

          <div style="margin-bottom: 25px; page-break-inside: avoid;">
            <h2 style="font-size: 16px; color: #34495e; border-bottom: 2px solid #3498db; padding-bottom: 5px;">二、建模数据（校正集、验证集）</h2>
            <table style="width: 100%; border-collapse: collapse; margin-top: 10px;">
              <tr><td style="border: 1px solid #ddd; padding: 8px; width: 30%; background: #f8f9fa;">样品属性</td><td style="border: 1px solid #ddd; padding: 8px;">${
                this.reportData.modelConstructInfo.sampleProperty
              }</td></tr>
              <tr><td style="border: 1px solid #ddd; padding: 8px; background: #f8f9fa;">样品总数量</td><td style="border: 1px solid #ddd; padding: 8px;">${
                this.reportData.modelConstructInfo.totalSampleCount
              }</td></tr>
              <tr><td style="border: 1px solid #ddd; padding: 8px; background: #f8f9fa;">样品制备方法</td><td style="border: 1px solid #ddd; padding: 8px;">${
                this.reportData.modelConstructInfo.samplePreparationMethod
              }</td></tr>
              <tr><td style="border: 1px solid #ddd; padding: 8px; background: #f8f9fa;">光谱采样仪器</td><td style="border: 1px solid #ddd; padding: 8px;">${
                this.reportData.modelConstructInfo.spectralSamplingInstrument
              }</td></tr>
              <tr><td style="border: 1px solid #ddd; padding: 8px; background: #f8f9fa;">光谱采样方法</td><td style="border: 1px solid #ddd; padding: 8px;">${
                this.reportData.modelConstructInfo.spectralSamplingMethod
              }</td></tr>
              <tr><td style="border: 1px solid #ddd; padding: 8px; background: #f8f9fa;">光谱采样时间</td><td style="border: 1px solid #ddd; padding: 8px;">${
                this.reportData.modelConstructInfo.spectralSamplingTime
              }</td></tr>
              <tr><td style="border: 1px solid #ddd; padding: 8px; background: #f8f9fa;">标准值测定仪器</td><td style="border: 1px solid #ddd; padding: 8px;">${
                this.reportData.modelConstructInfo.standardValueInstrument
              }</td></tr>
              <tr><td style="border: 1px solid #ddd; padding: 8px; background: #f8f9fa;">标准值测定方法</td><td style="border: 1px solid #ddd; padding: 8px;">${
                this.reportData.modelConstructInfo.standardValueMethod
              }</td></tr>
              <tr><td style="border: 1px solid #ddd; padding: 8px; background: #f8f9fa;">标准值范围</td><td style="border: 1px solid #ddd; padding: 8px;">${
                this.reportData.modelConstructInfo.standardValueRange
              }</td></tr>
            </table>
            ${
              chartImages.preprocessSpectrum
                ? `<div style="margin-top: 15px; text-align: center; page-break-inside: avoid; break-inside: avoid;"><img src="${chartImages.preprocessSpectrum}" style="max-width: 100%; height: auto; display: block; margin: 0 auto;" alt="校正集、验证集光谱吸光度曲线"/><p style="margin: 5px 0; font-size: 11px; color: #666;">校正集、验证集光谱吸光度曲线</p></div>`
                : ""
            }
          </div>

          <div style="margin-bottom: 25px; page-break-inside: avoid;">
            <h2 style="font-size: 16px; color: #34495e; border-bottom: 2px solid #3498db; padding-bottom: 5px;">三、背景光谱</h2>
            <table style="width: 100%; border-collapse: collapse; margin-top: 10px;">
              <tr><td style="border: 1px solid #ddd; padding: 8px; width: 30%; background: #f8f9fa;">光谱采样仪器</td><td style="border: 1px solid #ddd; padding: 8px;">${
                this.reportData.backgroundSpectrum.spectralSamplingInstrument
              }</td></tr>
              <tr><td style="border: 1px solid #ddd; padding: 8px; background: #f8f9fa;">光谱采样时间</td><td style="border: 1px solid #ddd; padding: 8px;">${
                this.reportData.backgroundSpectrum.spectralSamplingTime
              }</td></tr>
            </table>
          </div>

          <div style="margin-bottom: 25px; page-break-inside: avoid;">
            <h2 style="font-size: 16px; color: #34495e; border-bottom: 2px solid #3498db; padding-bottom: 5px;">四、数据统计处理工具</h2>
            <p>Python==3.11.11</p>
            <p>numpy==1.26.0</p>
            <p>scikit-learn==1.3.0</p>
            <p>pandas==2.1.1</p>
            <p>scipy==1.11.2</p>
          </div>

          <div style="margin-bottom: 25px; page-break-inside: avoid;">
            <h2 style="font-size: 16px; color: #34495e; border-bottom: 2px solid #3498db; padding-bottom: 5px;">五、异常剔除方法</h2>
            <p><strong>方法名称及参数设置：</strong>${
              this.reportData.outlierRemoval.methodName
            }</p>
            <p><strong>异常样本：</strong>${this.reportData.outlierRemoval.outlierSamples.join(
              "、"
            )}</p>
          </div>

          <div style="margin-bottom: 25px; page-break-inside: avoid;">
            <h2 style="font-size: 16px; color: #34495e; border-bottom: 2px solid #3498db; padding-bottom: 5px;">六、预处理方法</h2>
            <p><strong>方法名称及其参数设置：</strong>${
              this.reportData.preprocessing.methodNameAndParams
            }</p>
            ${
              chartImages.preprocessSpectrum
                ? `<div style="margin-top: 15px; text-align: center; page-break-inside: avoid; break-inside: avoid;"><img src="${chartImages.preprocessSpectrum}" style="max-width: 100%; height: auto; display: block; margin: 0 auto;" alt="完成预处理光谱吸光度曲线"/><p style="margin: 5px 0; font-size: 11px; color: #666;">完成预处理光谱吸光度曲线</p></div>`
                : ""
            }
          </div>

          <div style="margin-bottom: 25px; page-break-inside: avoid;">
            <h2 style="font-size: 16px; color: #34495e; border-bottom: 2px solid #3498db; padding-bottom: 5px;">七、波长选择方法</h2>
            <p><strong>方法名称及其参数设置：</strong>${
              this.reportData.wavelengthSelection.methodNameAndParams
            }</p>
            ${
              chartImages.wavelengthSpectrum
                ? `<div style="margin-top: 15px; text-align: center; page-break-inside: avoid; break-inside: avoid;"><img src="${chartImages.wavelengthSpectrum}" style="max-width: 100%; height: auto; display: block; margin: 0 auto;" alt="完成波长选择光谱吸光度曲线"/><p style="margin: 5px 0; font-size: 11px; color: #666;">完成波长选择光谱吸光度曲线</p></div>`
                : ""
            }
          </div>

          <div style="margin-bottom: 25px; page-break-inside: avoid;">
            <h2 style="font-size: 16px; color: #34495e; border-bottom: 2px solid #3498db; padding-bottom: 5px;">八、训练模型方法</h2>
            <p><strong>交叉验证集比例（训练集/测试集）：</strong>${
              this.reportData.trainingMethod.crossValidationRatio
            }</p>
            <p><strong>最大潜在变量数：</strong>${
              this.reportData.trainingMethod.maxLatentVariables
            }</p>
            ${
              chartImages.modelEvaluation
                ? `<div style="margin-top: 15px; text-align: center; page-break-inside: avoid; break-inside: avoid;"><img src="${chartImages.modelEvaluation}" style="max-width: 100%; height: auto; display: block; margin: 0 auto;" alt="训练模型评价曲线"/><p style="margin: 5px 0; font-size: 11px; color: #666;">训练模型评价曲线（横轴为潜在变量数）</p></div>`
                : ""
            }
          </div>

          <div style="margin-bottom: 25px; page-break-inside: avoid;">
            <h2 style="font-size: 16px; color: #34495e; border-bottom: 2px solid #3498db; padding-bottom: 5px;">九、本模型评价指标</h2>
            <table style="width: 100%; border-collapse: collapse; margin-top: 10px;">
              <tr><td style="border: 1px solid #ddd; padding: 8px; width: 30%; background: #f8f9fa;">模型潜在变量数</td><td style="border: 1px solid #ddd; padding: 8px;">${
                this.reportData.modelEvaluation.latentVariables
              }</td></tr>
              <tr><td style="border: 1px solid #ddd; padding: 8px; background: #f8f9fa;">Rc²</td><td style="border: 1px solid #ddd; padding: 8px;">${
                this.reportData.modelEvaluation.rcSquared
              }</td></tr>
              <tr><td style="border: 1px solid #ddd; padding: 8px; background: #f8f9fa;">RMSECV</td><td style="border: 1px solid #ddd; padding: 8px;">${
                this.reportData.modelEvaluation.rmsecv
              }</td></tr>
              <tr><td style="border: 1px solid #ddd; padding: 8px; background: #f8f9fa;">Rp²</td><td style="border: 1px solid #ddd; padding: 8px;">${
                this.reportData.modelEvaluation.rpSquared
              }</td></tr>
              <tr><td style="border: 1px solid #ddd; padding: 8px; background: #f8f9fa;">RMSEP</td><td style="border: 1px solid #ddd; padding: 8px;">${
                this.reportData.modelEvaluation.rmsep
              }</td></tr>
              <tr><td style="border: 1px solid #ddd; padding: 8px; background: #f8f9fa;">RPD</td><td style="border: 1px solid #ddd; padding: 8px;">${
                this.reportData.modelEvaluation.rpd
              }</td></tr>
              <tr><td style="border: 1px solid #ddd; padding: 8px; background: #f8f9fa;">T检验（边界值）</td><td style="border: 1px solid #ddd; padding: 8px;">${
                this.reportData.modelEvaluation.tTest
              }（${this.reportData.modelEvaluation.boundaryValue}）</td></tr>
            </table>
          </div>

          <div style="margin-bottom: 25px; page-break-inside: avoid;">
            <h2 style="font-size: 16px; color: #34495e; border-bottom: 2px solid #3498db; padding-bottom: 5px;">十、本模型相关性曲线</h2>
            ${
              chartImages.screePlot
                ? `<div style="margin-top: 15px; text-align: center; page-break-inside: avoid; break-inside: avoid;"><img src="${chartImages.screePlot}" style="max-width: 100%; height: auto; display: block; margin: 0 auto;" alt="碎石图"/><p style="margin: 5px 0; font-size: 11px; color: #666;">碎石图</p></div>`
                : ""
            }
            ${
              chartImages.scorePlot
                ? `<div style="margin-top: 15px; text-align: center; page-break-inside: avoid; break-inside: avoid;"><img src="${chartImages.scorePlot}" style="max-width: 100%; height: auto; display: block; margin: 0 auto;" alt="得分图"/><p style="margin: 5px 0; font-size: 11px; color: #666;">得分图</p></div>`
                : ""
            }
          </div>
        `;

        // 将容器添加到body中
        document.body.appendChild(reportContainer);

        // 创建高清PDF生成函数，支持智能分页
        const customDownloadPDF = (element, fileName) => {
          return import("html2canvas").then((html2canvas) => {
            return html2canvas
              .default(element, {
                useCORS: true,
                allowTaint: true,
                scale: 2, // 提高到4倍分辨率，增强清晰度
                backgroundColor: "#ffffff",
                width: element.scrollWidth,
                height: element.scrollHeight,
                scrollX: 0,
                scrollY: 0,
                // 优化渲染质量
                logging: false,
                imageTimeout: 15000,
                removeContainer: true,
              })
              .then((canvas) => {
                return import("jspdf").then((jsPDFModule) => {
                  const jsPDF = jsPDFModule.default;
                  const PDF = new jsPDF({
                    orientation: "p",
                    unit: "mm",
                    format: "a4",
                  });

                  const ctx = canvas.getContext("2d");
                  const a4w = 190; // A4宽度减去边距
                  const a4h = 277; // A4高度减去边距

                  // 智能分页算法
                  const imgWidth = a4w;
                  const imgHeight = (canvas.height * a4w) / canvas.width;

                  // 如果内容高度小于等于一页，直接添加
                  if (imgHeight <= a4h) {
                    PDF.addImage(
                      canvas.toDataURL("image/png", 0.5),
                      "PNG",
                      10,
                      10,
                      imgWidth,
                      imgHeight
                    );
                  } else {
                    // 智能分页：寻找合适的分页点
                    const pageHeight = a4h;
                    const canvasPageHeight = (pageHeight * canvas.width) / a4w;
                    let currentY = 0;
                    let pageCount = 0;

                    while (currentY < canvas.height) {
                      if (pageCount > 0) {
                        PDF.addPage();
                      }

                      // 计算当前页应该包含的canvas高度
                      let nextPageY = Math.min(
                        currentY + canvasPageHeight,
                        canvas.height
                      );

                      // 检查是否会在图片中间分页（简单的启发式方法）
                      // 如果剩余高度小于总高度的10%，则将其包含在当前页
                      if (
                        canvas.height - nextPageY < canvas.height * 0.1 &&
                        canvas.height - nextPageY > 0
                      ) {
                        nextPageY = canvas.height;
                      }

                      const actualPageHeight = nextPageY - currentY;

                      // 创建当前页的canvas
                      const pageCanvas = document.createElement("canvas");
                      pageCanvas.width = canvas.width;
                      pageCanvas.height = actualPageHeight;

                      const pageCtx = pageCanvas.getContext("2d");
                      pageCtx.fillStyle = "#ffffff";
                      pageCtx.fillRect(
                        0,
                        0,
                        pageCanvas.width,
                        pageCanvas.height
                      );

                      // 从原canvas复制当前页的内容
                      pageCtx.drawImage(
                        canvas,
                        0,
                        currentY, // 源起始位置
                        canvas.width,
                        actualPageHeight, // 源尺寸
                        0,
                        0, // 目标起始位置
                        pageCanvas.width,
                        pageCanvas.height // 目标尺寸
                      );

                      // 计算在PDF中的显示高度
                      const displayHeight =
                        (actualPageHeight * a4w) / canvas.width;

                      PDF.addImage(
                        pageCanvas.toDataURL("image/png", 1.0),
                        "PNG",
                        10,
                        10,
                        imgWidth,
                        Math.min(displayHeight, pageHeight)
                      );

                      currentY = nextPageY;
                      pageCount++;
                    }
                  }

                  PDF.save(fileName);
                });
              });
          });
        };

        // 生成PDF
        const fileName = `建模报告_${
          this.reportData.modelBaseInfo.modelNo || "model"
        }.pdf`;
        await customDownloadPDF(reportContainer, fileName);

        // 清理临时容器
        setTimeout(() => {
          document.body.removeChild(reportContainer);
        }, 1000);

        this.$message.success("PDF报告已生成并下载");
      } catch (error) {
        console.error("生成PDF报告失败:", error);
        this.$message.error("生成PDF报告失败: " + error.message);
      }
    },

    // 添加章节标题到PDF
    addSectionToPDF(pdf, title, y) {
      pdf.setFontSize(14);
      pdf.setFont("helvetica", "bold");
      pdf.text(title, 20, y);
      return y + 5;
    },

    // 添加表格到PDF
    addTableToPDF(pdf, data, startY, contentWidth, margin) {
      let currentY = startY;
      const rowHeight = 8;
      const col1Width = 50;
      const col2Width = contentWidth - col1Width;

      pdf.setFontSize(10);
      pdf.setFont("helvetica", "normal");

      data.forEach((row) => {
        // 检查是否需要新页面
        if (currentY > 270) {
          pdf.addPage();
          currentY = 20;
        }

        // 绘制表格边框
        pdf.rect(margin, currentY - 6, col1Width, rowHeight);
        pdf.rect(margin + col1Width, currentY - 6, col2Width, rowHeight);

        // 添加文本
        pdf.setFont("helvetica", "bold");
        pdf.text(row[0], margin + 2, currentY);
        pdf.setFont("helvetica", "normal");
        pdf.text(String(row[1]), margin + col1Width + 2, currentY);

        currentY += rowHeight;
      });

      return currentY;
    },

    // 添加图表到PDF
    async addChartToPDF(pdf, imageBase64, title, startY, contentWidth, margin) {
      let currentY = startY;

      // 检查是否需要新页面
      if (currentY > 200) {
        pdf.addPage();
        currentY = 20;
      }

      // 添加图表标题
      pdf.setFontSize(12);
      pdf.setFont("helvetica", "bold");
      pdf.text(title, margin, currentY);
      currentY += 10;

      try {
        // 添加图片
        const imgWidth = contentWidth;
        const imgHeight = 100; // 固定高度

        pdf.addImage(imageBase64, "PNG", margin, currentY, imgWidth, imgHeight);
        currentY += imgHeight + 10;
      } catch (error) {
        console.warn("添加图表失败:", error);
        pdf.setFontSize(10);
        pdf.text("[图表无法显示]", margin, currentY);
        currentY += 15;
      }

      return currentY;
    },

    // HTML回退方案
    generateHTMLFallback(chartImages) {
      const reportContent = this.generateReportHTMLContent(chartImages);
      try {
        const blob = new Blob([reportContent], { type: "text/html" });
        const url = URL.createObjectURL(blob);
        const link = document.createElement("a");
        link.href = url;
        link.download = `建模报告_${this.reportData.modelBaseInfo.modelNo}.html`;
        link.click();
        URL.revokeObjectURL(url);

        this.$message.success("报告已生成并下载（HTML格式）");

        this.$confirm("报告已生成，是否现在预览?", "提示", {
          confirmButtonText: "预览",
          cancelButtonText: "关闭",
          type: "success",
        })
          .then(() => {
            const previewUrl = URL.createObjectURL(blob);
            window.open(previewUrl, "_blank");
          })
          .catch(() => {});
      } catch (error) {
        console.error("生成报告失败:", error);
        this.$message.error("生成报告失败: " + error.message);
      }
    },

    // 生成报告 HTML 内容
    generateReportHTMLContent(chartImages) {
      return `<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>PLS定量模型建立与验证报告</title>
  <style>
    body { font-family: 'Microsoft YaHei', Arial, sans-serif; margin: 20px; line-height: 1.6; }
    h1 { color: #2c3e50; text-align: center; border-bottom: 3px solid #3498db; padding-bottom: 10px; }
    h2 { color: #34495e; border-bottom: 2px solid #3498db; padding-bottom: 5px; margin-top: 30px; }
    h3 { color: #7f8c8d; margin-top: 20px; }
    .info-table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
    .info-table td { padding: 8px; border: 1px solid #ddd; }
    .info-table td:first-child { background-color: #f8f9fa; font-weight: bold; width: 150px; }
    .chart-container { text-align: center; margin: 20px 0; }
    .chart-container img { max-width: 100%; height: auto; border: 1px solid #ddd; }
    .section { margin-bottom: 30px; }
  </style>
</head>
<body>
  <h1>PLS定量模型建立与验证报告</h1>
  
  <div class="section">
    <h2>一、模型概要</h2>
    <table class="info-table">
      <tr><td>模型名称</td><td>${
        this.reportData.modelBaseInfo.modelName
      }</td></tr>
      <tr><td>模型编号</td><td>${
        this.reportData.modelBaseInfo.modelNo
      }</td></tr>
      <tr><td>分析成分名称</td><td>${
        this.reportData.modelBaseInfo.componentName
      }</td></tr>
      <tr><td>预测值范围</td><td>${
        this.reportData.modelBaseInfo.predict_min
      }%~${this.reportData.modelBaseInfo.predict_max}%</td></tr>
      <tr><td>建立人</td><td>${this.reportData.modelBaseInfo.creator}</td></tr>
      <tr><td>建立时间</td><td>${
        this.reportData.modelBaseInfo.createTime
      }</td></tr>
    </table>
  </div>

  <div class="section">
    <h2>二、建模数据（校正集、验证集）</h2>
    <table class="info-table">
      <tr><td>样品属性</td><td>${
        this.reportData.modelConstructInfo.sampleProperty
      }</td></tr>
      <tr><td>样品总数量</td><td>${
        this.reportData.modelConstructInfo.totalSampleCount
      }</td></tr>
      <tr><td>样品制备方法</td><td>${
        this.reportData.modelConstructInfo.samplePreparationMethod
      }</td></tr>
      <tr><td>光谱采样仪器</td><td>${
        this.reportData.modelConstructInfo.spectralSamplingInstrument
      }</td></tr>
      <tr><td>光谱采样方法</td><td>${
        this.reportData.modelConstructInfo.spectralSamplingMethod
      }</td></tr>
      <tr><td>光谱采样时间</td><td>${
        this.reportData.modelConstructInfo.spectralSamplingTime
      }</td></tr>
      <tr><td>标准值测定仪器</td><td>${
        this.reportData.modelConstructInfo.standardValueInstrument
      }</td></tr>
      <tr><td>标准值测定方法</td><td>${
        this.reportData.modelConstructInfo.standardValueMethod
      }</td></tr>
      <tr><td>标准值范围</td><td>${
        this.reportData.modelConstructInfo.standardValueRange
      }</td></tr>
    </table>
    ${
      chartImages.preprocessSpectrum
        ? `
    <h3>校正集、验证集光谱吸光度曲线</h3>
    <div class="chart-container">
      <img src="${chartImages.preprocessSpectrum}" alt="预处理光谱吸光度曲线" />
    </div>
    `
        : ""
    }
  </div>

  <div class="section">
    <h2>三、背景光谱</h2>
    <table class="info-table">
      <tr><td>光谱采样仪器</td><td>${
        this.reportData.backgroundSpectrum.spectralSamplingInstrument
      }</td></tr>
      <tr><td>光谱采样时间</td><td>${
        this.reportData.backgroundSpectrum.spectralSamplingTime
      }</td></tr>
    </table>
  </div>

  <div class="section">
    <h2>四、数据统计处理工具</h2>
    <p>Python==3.11.11</p><p>numpy==1.26.0</p><p>scikit-learn==1.3.0</p><p>pandas==2.1.1</p><p>scipy==1.11.2</p>
  </div>

  <div class="section">
    <h2>五、异常剔除方法</h2>
    <p><strong>方法名称及参数设置：</strong>${
      this.reportData.outlierRemoval.methodName
    }</p>
    <p><strong>异常样本：</strong>${this.reportData.outlierRemoval.outlierSamples.join(
      "、"
    )}</p>
  </div>

  <div class="section">
    <h2>六、预处理方法</h2>
    <p><strong>方法名称及其参数设置：</strong>${
      this.reportData.preprocessing.methodNameAndParams
    }</p>
    ${
      chartImages.preprocessSpectrum
        ? `<h3>完成预处理光谱吸光度曲线</h3><div class="chart-container"><img src="${chartImages.preprocessSpectrum}" alt="预处理光谱吸光度曲线" /></div>`
        : ""
    }
  </div>

  <div class="section">
    <h2>七、波长选择方法</h2>
    <p><strong>方法名称及其参数设置：</strong>${
      this.reportData.wavelengthSelection.methodNameAndParams
    }</p>
    ${
      chartImages.wavelengthSpectrum
        ? `<h3>完成波长选择光谱吸光度曲线</h3><div class="chart-container"><img src="${chartImages.wavelengthSpectrum}" alt="波长选择光谱吸光度曲线" /></div>`
        : ""
    }
  </div>

  <div class="section">
    <h2>八、训练模型方法</h2>
    <p><strong>交叉验证集比例（训练集/测试集）：</strong>${
      this.reportData.trainingMethod.crossValidationRatio
    }</p>
    <p><strong>最大潜在变量数：</strong>${
      this.reportData.trainingMethod.maxLatentVariables
    }</p>
    ${
      chartImages.modelEvaluation
        ? `<h3>训练模型评价曲线（横轴为潜在变量数）</h3><div class="chart-container"><img src="${chartImages.modelEvaluation}" alt="模型评价曲线" /></div>`
        : ""
    }
  </div>

  <div class="section">
    <h2>九、本模型评价指标</h2>
    <table class="info-table">
      <tr><td>模型潜在变量数</td><td>${
        this.reportData.modelEvaluation.latentVariables
      }</td></tr>
      <tr><td>Rc²</td><td>${this.reportData.modelEvaluation.rcSquared}</td></tr>
      <tr><td>RMSECV</td><td>${this.reportData.modelEvaluation.rmsecv}</td></tr>
      <tr><td>Rp²</td><td>${this.reportData.modelEvaluation.rpSquared}</td></tr>
      <tr><td>RMSEP</td><td>${this.reportData.modelEvaluation.rmsep}</td></tr>
      <tr><td>RPD</td><td>${this.reportData.modelEvaluation.rpd}</td></tr>
      <tr><td>T检验（边界值）</td><td>${
        this.reportData.modelEvaluation.tTest
      }（${this.reportData.modelEvaluation.boundaryValue}）</td></tr>
    </table>
  </div>

  <div class="section">
    <h2>十、本模型相关性曲线</h2>
    ${
      chartImages.screePlot
        ? `<h3>碎石图</h3><div class="chart-container"><img src="${chartImages.screePlot}" alt="碎石图" /></div>`
        : ""
    }
    ${
      chartImages.loadingPlot
        ? `<h3>负载图</h3><div class="chart-container"><img src="${chartImages.loadingPlot}" alt="负载图" /></div>`
        : ""
    }
    ${
      chartImages.scorePlot
        ? `<h3>得分图</h3><div class="chart-container"><img src="${chartImages.scorePlot}" alt="得分图" /></div>`
        : ""
    }
  </div>

  <div class="section">
    <h2>十一、主成分分析</h2>
    <p>此处为 PCA 图表与说明位置</p>
  </div>
</body>
</html>`;
    },

    async saveModel() {
      if (!this.selectedModelDetail) {
        this.$message.warning("请先选择要保存的模型详情");
        return;
      }

      // 根据preprocessing_sequence获取预处理方法名称
      const preMethodName = this.getPreprocessingMethodNames();

      // 获取波长选择方法名称
      const waveSelectMethodName = this.getWavelengthSelectionMethodName();

      // 构建模型数据对象
      const rc2 = Number(this.selectedModelDetail.rc2);

      // TODO 需要传入 is_merge（合并基团波长） 和 group_information （基团信息）字段
      this.modelData = {
        modelId: parseInt(this.modelId) || this.modelId, // 确保是数字类型
        modelNo: this.modelNo, // 模型编号
        modelStateCode: "02",
        modelStateName: "已完成",
        cv_ratio_name: this.trainingParams.crossValidationRatio, // 使用下划线命名
        preMethodName: preMethodName, // 使用下划线命名
        waveSelectMethodName: waveSelectMethodName, // 使用下划线命名
        maxPotential: Number(this.selectedModelDetail.latentVariableCount) || 0, // 使用下划线命名
        rc2: Number(this.selectedModelDetail.rc2) || 0.0,
        rmsecv: Number(this.selectedModelDetail.rmsecv) || 0.0,
        rp2: Number(this.selectedModelDetail.rp2) || 0.0,
        rmsep: Number(this.selectedModelDetail.rmsep) || 0.0,
        rpd: Number(this.selectedModelDetail.rpd) || 0.0,
        tTest: Number(this.selectedModelDetail.t_value) || 0.0, // 使用下划线命名
        // 添加 constructNo 和 samplingTime 字段
        constructNo: this.selectedConstructData ? this.selectedConstructData.constructNo : null,
        samplingTime: this.selectedConstructData ? this.selectedConstructData.samplingTime : null,
      };

      // 数据验证
      if (!this.modelNo) {
        this.$message.error("模型编号不能为空");
        return;
      }

      if (!preMethodName) {
        this.$message.warning("请先完成数据预处理步骤");
        return;
      }

      if (!waveSelectMethodName) {
        this.$message.warning("请先完成波长选择步骤");
        return;
      }

      try {
        const res = await updateModelData(this.modelData);

        if (res && res.code === 200) {
          this.$message.success("模型详情保存成功");
          this.currentStep = 6; // 更新到完成步骤
          console.log("✅ 模型保存成功，步骤更新为:", this.currentStep);
        } else {
          const errorMsg = res?.message || res?.msg || "未知错误";
          console.error("❌ 保存失败，后端返回:", res);
          this.$message.error("模型详情保存失败：" + errorMsg);
        }
      } catch (error) {
        console.error("❌ 保存模型数据时发生错误:", error);
        console.error("❌ 错误详情:", {
          message: error.message,
          response: error.response,
          request: error.request,
        });

        let errorMessage = "保存失败：";
        if (error.response && error.response.data) {
          errorMessage +=
            error.response.data.message ||
            error.response.data.msg ||
            error.response.statusText;
        } else {
          errorMessage += error.message || error.toString();
        }

        this.$message.error(errorMessage);
      }

      this.$message({
        message: `已选择模型详情：潜在变量数${this.selectedModelDetail.latentVariableCount}，准备保存...`,
        type: "success",
      });

      // 1、保存预处理过程信息
      await this.savePreprocessingData();

      // 2、保存波长选择信息
      await this.saveWaveSelectData();

      // 3、保存模型详情数据
      await this.saveModelDetailData();

      // 3、取消模型详情被选中的那行
      this.selectedModelDetail = null;
      this.$refs.modelDetailTable.clearSelection();
    },

    // 根据preprocessing_sequence获取预处理方法名称
    getPreprocessingMethodNames() {
      if (
        !this.preProcessingPayload ||
        !this.preProcessingPayload.preprocessing_sequence
      ) {
        return "";
      }

      const methodNames = [];

      // 遍历预处理序列
      this.preProcessingPayload.preprocessing_sequence.forEach((item) => {
        if (item.root && item.root.method) {
          // 在methodList中查找对应的方法名称
          const method = this.methodList.find(
            (m) => m.e_Name === item.root.method
          );
          if (method) {
            methodNames.push(method.name);
          }
        }
      });

      // 用 " + " 连接方法名称
      return methodNames.join(" , ");
    },

    // 获取波长选择方法名称
    getWavelengthSelectionMethodName() {
      // 查找当前启用的波长选择方法
      const selectedMethod = this.wavelengthChooseList.find(
        (item) => item.enabled
      );
      return selectedMethod ? selectedMethod.name : "";
    },

    // 保存预处理过程数据
    async savePreprocessingData() {
      try {
        // 检查必要的数据是否存在
        if (
          !this.preProcessing_result ||
          !this.preProcessingPayload.preprocessing_sequence
        ) {
          console.warn("预处理数据不完整，跳过保存");
          return;
        }

        // 构建预处理结果参数（排除X_preprocessed，只保存过程参数）
        const preprocessingResult = {};

        // 提取所有非X_preprocessed的参数
        Object.keys(this.preProcessing_result).forEach((key) => {
          if (
            key !== "X_preprocessed" &&
            this.preProcessing_result[key] !== undefined &&
            this.preProcessing_result[key] !== null
          ) {
            preprocessingResult[key] = this.preProcessing_result[key];
          }
        });

        // 构建预处理数据对象
        this.preProcessing_data = {
          modelId: parseInt(this.modelId) || this.modelId,
          datasetId: this.selectedConstructData?.constructId || null,
          preprocessingSequence: JSON.stringify(
            this.preProcessingPayload.preprocessing_sequence
          ),
          preprocessingResult:
            Object.keys(preprocessingResult).length > 0
              ? JSON.stringify(preprocessingResult)
              : null,
        };

        console.log("🚀 准备保存预处理数据:", this.preProcessing_data);

        // 调用API保存预处理数据
        const response = await addPreprocessing(this.preProcessing_data);

        if (response && response.code === 200) {
          console.log("✅ 预处理数据保存成功");
          this.$message.success("预处理过程信息保存成功");
        } else {
          console.error("❌ 预处理数据保存失败:", response);
          this.$message.warning(
            "预处理过程信息保存失败：" +
              (response?.message || response?.msg || "未知错误")
          );
        }
      } catch (error) {
        console.error("❌ 保存预处理数据时发生错误:", error);
        this.$message.warning(
          "预处理过程信息保存失败：" + (error.message || error.toString())
        );
      }
    },

    // 保存波长选择数据
    async saveWaveSelectData() {
      try {
        // 检查必要的数据是否存在
        if (!this.waveSelect_data || !this.selected_indices || !this.mask) {
          console.warn("波长选择数据不完整，跳过保存");
          return;
        }

        // 构建波长选择数据对象
        const waveSelectSaveData = {
          modelId: parseInt(this.modelId) || this.modelId,
          datasetId: this.selectedConstructData?.constructId || null,
          waveSelect: JSON.stringify(this.waveSelect_data),
          selectedIndices: JSON.stringify(this.selected_indices),
          // mask: JSON.stringify(this.mask),
          // selectedWavelengths: JSON.stringify(this.chooseWavelength),
        };

        console.log("🚀 准备保存波长选择数据:", waveSelectSaveData);

        // 调用API保存波长选择数据
        const response = await addWaveSelect(waveSelectSaveData);

        if (response && response.code === 200) {
          console.log("✅ 波长选择数据保存成功");
          this.$message.success("波长选择信息保存成功");
        } else {
          console.error("❌ 波长选择数据保存失败:", response);
          this.$message.warning(
            "波长选择信息保存失败：" +
              (response?.message || response?.msg || "未知错误")
          );
        }
      } catch (error) {
        console.error("❌ 保存波长选择数据时发生错误:", error);
        this.$message.warning(
          "波长选择信息保存失败：" + (error.message || error.toString())
        );
      }
    },

    // 调试方法：直接测试预处理数据保存
    async debugSavePreprocessing() {
      console.log("🔧 开始调试预处理数据保存功能...");

      // 模拟预处理数据，用于调试
      this.preProcessing_result = {
        X_preprocessed: [
          [1, 2, 3],
          [4, 5, 6],
        ], // 这个不会被保存
        x_center_mean: [0.1, 0.2, 0.3],
        x_msc_mean: [0.4, 0.5, 0.6],
        x_standar_mean: [0.7, 0.8, 0.9],
        x_standar_scale: [1.0, 1.1, 1.2],
        x_nor_max: [2.0, 2.1, 2.2],
        x_nor_min: [0.01, 0.02, 0.03],
      };

      // 模拟预处理方法序列
      this.preProcessingPayload = {
        preprocessing_sequence: [
          {
            root: {
              type: "baseline",
              method: "diff_derivative",
              params: { deriv_order: 1 },
            },
          },
          {
            root: {
              type: "scaling",
              method: "centering",
            },
          },
        ],
      };

      // 模拟选中的建模数据
      this.selectedConstructData = {
        constructId: 1,
      };

      console.log("🔧 模拟数据已设置:");
      console.log("  - preProcessing_result:", this.preProcessing_result);
      console.log("  - preProcessingPayload:", this.preProcessingPayload);
      console.log("  - selectedConstructData:", this.selectedConstructData);
      console.log("  - modelId:", this.modelId);

      // 调用保存方法
      await this.savePreprocessingData();

      console.log("🔧 调试完成！");
    },

    // 调试方法：直接测试波长选择数据保存
    async debugSaveWaveSelect() {
      console.log("🔧 开始调试波长选择数据保存功能...");

      // 模拟波长选择数据，用于调试
      this.waveSelect_data = [
        {
          root: {
            method: "SiPLS",
            params: {
              n_intervals: 3,
              n_select: 2,
              n_components: 2,
            },
          },
        },
      ];

      // 模拟选择的波长索引
      this.selected_indices = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19];

      // 模拟掩码
      this.mask = [
        false,
        true,
        false,
        true,
        false,
        true,
        false,
        true,
        false,
        true,
        false,
        true,
        false,
        true,
        false,
        true,
        false,
        true,
        false,
        true,
      ];

      // 模拟选择的波长点
      this.chooseWavelength = [
        "400",
        "402",
        "404",
        "406",
        "408",
        "410",
        "412",
        "414",
        "416",
        "418",
      ];

      // 模拟选中的建模数据
      this.selectedConstructData = {
        constructId: 1,
      };

      console.log("🔧 模拟数据已设置:");
      console.log("  - waveSelect_data:", this.waveSelect_data);
      console.log("  - selected_indices:", this.selected_indices);
      console.log("  - mask:", this.mask);
      console.log("  - chooseWavelength:", this.chooseWavelength);
      console.log("  - selectedConstructData:", this.selectedConstructData);
      console.log("  - modelId:", this.modelId);

      // 调用保存方法
      await this.saveWaveSelectData();

      console.log("🔧 调试完成！");
    },
    handleTraining() {
      this.confirmStepAction(
        "您已经进行到后续步骤，重新训练模型将清空后续所有数据，是否继续？",
        4, // 目标步骤为4（训练模型）
        () => {
          // 检查是否已完成前面的步骤
          if (!this.mask || !this.selected_indices) {
            this.$message.warning("请先完成波长选择步骤");
            return;
          }
          this.trainingParamsVisible = true;
        }
      );
    },

    // 减少潜在变量数
    decreaseLatentVariables() {
      if (this.trainingParams.maxLatentVariables > 1) {
        this.trainingParams.maxLatentVariables--;
      }
    },

    // 增加潜在变量数
    increaseLatentVariables() {
      if (this.trainingParams.maxLatentVariables < 20) {
        this.trainingParams.maxLatentVariables++;
      }
    },

    // 确认训练参数并开始训练
    async confirmTraining() {
      try {
        // 根据交叉验证比例计算折数
        const ratio = parseFloat(this.trainingParams.crossValidationRatio);
        let cvFolds;
        if (ratio === 0.3) cvFolds = 3;
        else if (ratio === 0.4) cvFolds = 4;
        else if (ratio === 0.5) cvFolds = 5;
        else cvFolds = 5; // 默认5折

        // 收集验证集数据
        let validationData = [];
        let validationStandardValues = [];

        try {
          const validationResponse = await listConstructDetail({
            constructNo: this.selectedConstructData.constructNo,
            constructdTypeName: "验证集",
            pageNum: 1,
            pageSize: 9999, // 设置一个足够大的数值
          });

          if (
            validationResponse.code === 200 &&
            validationResponse.rows.length > 0
          ) {
            console.log("验证集数据：", validationResponse.rows);

            // 仿照校正集的逻辑，处理验证集数据
            validationData = validationResponse.rows.map((row) => {
              const wavelengthArray = row.wavelength.split(",").map(Number);
              const standardValue = Number(row.standard);
              validationStandardValues.push(standardValue);

              // 返回波长数据（不包含标准值，因为标准值单独存储）
              return wavelengthArray;
            });

            console.log("处理后的验证集光谱数据:", validationData);
            console.log("验证集标准值:", validationStandardValues);
          } else {
            console.log("未找到验证集数据或验证集为空");
          }
        } catch (validationError) {
          console.warn("获取验证集数据失败:", validationError);
          // 验证集数据获取失败不影响训练，继续使用空数组
        }

        // 根据 selected_indices 从预处理结果中筛选对应的波长点
        // 注意：这里的selected_indices包含了波长选择算法+基团选择的综合结果
        if (this.preProcessing_result.X_preprocessed && this.selected_indices) {
          this.trainPayload.selected_x =
            this.preProcessing_result.X_preprocessed.map((row) => {
              // 对每一行光谱数据，根据 selected_indices 筛选出对应索引的波长点
              return this.selected_indices.map((index) => row[index]);
            });
          console.log(
            `筛选前: ${this.preProcessing_result.X_preprocessed.length}个样本，每个${this.preProcessing_result.X_preprocessed[0]?.length}个波长点`
          );
          console.log(
            `筛选后: ${this.trainPayload.selected_x.length}个样本，每个${this.trainPayload.selected_x[0]?.length}个波长点`
          );
          console.log(
            `selected_indices长度: ${this.selected_indices.length}，包含波长选择算法+基团选择的综合结果`
          );
        } else {
          console.error("预处理结果或波长选择索引缺失");
          this.trainPayload.selected_x =
            this.preProcessing_result.X_preprocessed;
        }
        this.trainPayload.train_y = this.standardValueList;
        this.trainPayload.test_x = validationData;
        this.trainPayload.test_y = validationStandardValues;
        this.trainPayload.vail_pre_sequence = this.methodList
          .filter((item) => item.enabled)
          .map((item) => {
            const payload = this.buildMethodPayload(item, true);
            console.log(
              `验证集预处理方法 ${item.name}:`,
              JSON.stringify(payload, null, 2)
            );
            return payload;
          });
        this.trainPayload.selected_indices = this.selected_indices || []; // 包含波长选择算法+基团选择的综合结果
        this.trainPayload.max_components =
          this.trainingParams.maxLatentVariables;
        this.trainPayload.cv_folds = cvFolds;

        console.log("训练请求数据this.trainPayload:", this.trainPayload);

        const response = await axios.post(
          "/train-model/train",
          this.trainPayload
        );
        console.log("训练结果:", response.data);

        // 检查响应状态，如果是错误状态则停止执行
        if (response.data && response.data.status === "error") {
          console.error("❌ 训练过程中发生错误:", response.data);

          // 提取错误信息
          let errorMessage = "训练失败";
          if (response.data.result && response.data.result.error) {
            errorMessage = response.data.result.error;
          } else if (response.data.error) {
            errorMessage = response.data.error;
          } else if (response.data.message) {
            errorMessage = response.data.message;
          }

          // 显示错误信息并停止执行
          this.$message.error("模型训练失败: " + errorMessage);
          this.trainingParamsVisible = false;
          return; // 停止执行后续代码
        }

        this.train_result = response.data;

        // 提取 critical_t 值并保留4位小数
        if (
          response.data &&
          response.data.result &&
          Array.isArray(response.data.result) &&
          response.data.result.length > 0 &&
          response.data.result[0] &&
          response.data.result[0].critical_t !== undefined
        ) {
          this.critical_t = Number(response.data.result[0].critical_t).toFixed(
            4
          );
          console.log("✅ 成功提取 critical_t 值:", this.critical_t);
        } else {
          console.warn("❌ critical_t 数据不存在或格式不正确:", response.data);
          this.critical_t = null;
        }

        // 更新模型评价表格数据
        if (response.data && response.data.result) {
          console.log("🔄 开始更新模型详情表格");
          this.updateModelDetailTable(response.data.result);
          console.log("✅ 模型详情表格更新完成");
        }

        this.$message.success("模型训练完成");
        this.currentStep = 5; // 更新到保存模型步骤
        this.trainingParamsVisible = false;
      } catch (error) {
        console.error("模型训练失败:", error);
        this.$message.error(
          "模型训练失败: " + (error.response?.data?.message || error.message)
        );
      }
    },

    // 更新模型详情表格数据
    updateModelDetailTable(trainResult) {
      console.log("updateModelDetailTable called with:", trainResult);

      // 根据训练结果更新表格数据
      // 这里需要根据实际的训练结果数据结构来调整
      if (trainResult) {
        this.modelDetailtableData = trainResult.map((detail, index) => ({
          modelNo: this.modelNo,
          latentVariableCount:
            detail.latentVariableCount || detail.n_components || index + 1, // 支持多种字段名
          rc2: (detail.rc2 || 0).toFixed(4),
          rmsecv: (detail.rmsecv || 0).toFixed(4),
          rp2: (detail.rp2 || 0).toFixed(4),
          rmsep: (detail.rmsep || 0).toFixed(4),
          rpd: (detail.rpd || 0).toFixed(4),
          t_value: (detail.t_value || 0).toFixed(4),
          coef: detail.coef || [],
          intercept: detail.intercept || 0,
          constituentId: detail.constituentId || 0,
          y_cv_pred: detail.y_cv_pred || 0,
          y_cv_true: detail.y_cv_true || 0,
          y_test_pred: detail.y_test_pred || 0,
          y_test_true: detail.y_test_true || 0,
        }));

        console.log("modelDetailtableData updated:", this.modelDetailtableData);
      }

      // 更新模型评价曲线数据
      if (trainResult.evaluation_curve) {
        this.xAxisData6 = trainResult.evaluation_curve.x || [];
        this.seriesData6 = trainResult.evaluation_curve.series || [];
      }
    },

    addModelDetail() {
      const modelList = [
        {
          modelNo: this.modelNo,
          latentVariableCount: 10,
          rc2: 0.95,
          rmsecv: 0.12,
          rp2: 0.94,
          rmsep: 0.15,
          rpd: 2.1,
          t_value: 5.32,
          t: 1.98,
          calibrationStandard: [1.1, 2.2, 3.3],
          calibrationPredicted: [1.0, 2.1, 3.2],
          validationPredicted: [1.2, 2.3, 3.4],
          coef: [0.1, 0.2, 0.3],
        },
        {
          modelNo: this.modelNo,
          latentVariableCount: 8,
          rc2: 0.92,
          rmsecv: 0.2,
          rp2: 0.91,
          rmsep: 0.18,
          rpd: 1.9,
          t_value: 4.87,
          t: 1.96,
          calibrationStandard: [2.1, 3.2, 4.3],
          calibrationPredicted: [2.0, 3.1, 4.2],
          validationPredicted: [2.2, 3.3, 4.4],
          coef: [0.15, 0.25, 0.35],
        },
      ];
      this.modelDetailtableData = modelList; // 将数据存储到模型明细表中
      saveModelData(modelList).then((res) => {
        if (res.code === 200) {
          this.$message({
            message: "模型训练成功",
            type: "success",
          });
        } else {
          this.$message({
            message: "模型训练失败",
            type: "error",
          });
        }
      });
    },
    getModelDetail() {
      getModelDetailData(this.modelNo).then((res) => {
        if (res) {
          console.log("模型明细数据：", res.data);
          this.$message({
            message: "模型明细查询成功",
            type: "success",
          });
        } else {
          this.$message({
            message: "模型明细查询失败或无数据",
            type: "error",
          });
        }
      });
    },
    deleteModelDetail() {
      delModelDetailData(this.modelNo).then((res) => {
        if (res.code === 200) {
          this.$message({
            message: "模型明细删除成功",
            type: "success",
          });
          // this.modelDetailtableData = []; // 清空表格数据
        } else {
          this.$message({
            message: "模型明细删除失败",
            type: "error",
          });
        }
      });
    },
    async handlePrincipalComponentAnalysis() {
      this.confirmStepAction(
        "您已经进行到后续步骤，重新进行主成分分析将清空后续所有数据，是否继续？",
        3, // 目标步骤为3（主成分分析）
        () => {
          // 检查是否已完成波长选择
          if (!this.mask || !this.selected_indices) {
            this.$message.warning("请先完成波长选择步骤");
            return;
          }
          this.executePCA();
        }
      );
    },

    // 执行主成分分析的逻辑（从原handlePrincipalComponentAnalysis方法中提取）
    async executePCA() {
      try {
        // 构建PCA请求数据 - 使用波长选择后的数据
        const pcaPayload = {
          wave_data: this.preProcessing_waveData.map((row) => {
            // 根据掩码筛选数据
            const [standard, ...wavelengths] = row;
            const selectedWavelengths = wavelengths.filter((_, index) => {
              return this.mask[index] === true || this.mask[index] === 1;
            });
            return [standard, ...selectedWavelengths];
          }),
        };

        // const pcaPayload = {
        //   wave_data: this.preProcessing_result.X_preprocessed.map(
        //     (row, index) => {
        //       return [this.standardValueList[index], ...row];
        //     }
        //   ).map((row) => {
        //     // 根据掩码筛选数据
        //     const [standard, ...wavelengths] = row;
        //     const selectedWavelengths = wavelengths.filter((_, index) => {
        //       return this.mask[index] === true || this.mask[index] === 1;
        //     });
        //     return [standard, ...selectedWavelengths];
        //   }),
        // };

        console.log("PCA请求数据:", pcaPayload);

        const pca_result = await axios.post("/train-model/pca", pcaPayload);
        console.log("PCA结果:", pca_result.data);

        // 更新组件数据
        this.scoreScatterPc1 = pca_result.data.result.score_scatter_pc1;
        this.scoreScatterPc2 = pca_result.data.result.score_scatter_pc2;
        this.pc1Value = pca_result.data.result.pc1_values * 100;
        this.pc2Value = pca_result.data.result.pc2_values * 100;

        // 更新椭圆参数
        this.ellipseAngle = pca_result.data.result.angle || 0;
        this.ellipseWidth = pca_result.data.result.width || 0;
        this.ellipseHeight = pca_result.data.result.height || 0;

        console.log("PCA散点图数据已更新:", {
          pc1: this.scoreScatterPc1,
          pc2: this.scoreScatterPc2,
          pc1Value: this.pc1Value,
          pc2Value: this.pc2Value,
        });

        // 更新负载图数据
        const loadings_pc1 = pca_result.data.result.loadings_scatter_pc1 || [];
        const loadings_pc2 = pca_result.data.result.loadings_scatter_pc2 || [];

        // 为负载图生成X轴数据（通常是波长索引或特征索引）
        this.xAxisData5 = loadings_pc1.map((_, index) =>
          (index + 1).toString()
        );

        // 为负载图生成系列数据
        this.seriesData5 = [
          {
            name: "PC1",
            type: "line",
            data: loadings_pc1,
            lineStyle: {
              color: "#FF4444",
              width: 2,
            },
            itemStyle: {
              color: "#FF4444",
            },
          },
          {
            name: "PC2",
            type: "line",
            data: loadings_pc2,
            lineStyle: {
              color: "#00AA00",
              width: 2,
            },
            itemStyle: {
              color: "#00AA00",
            },
          },
        ];

        console.log("负载图数据已更新:", {
          xAxisData5: this.xAxisData5,
          seriesData5: this.seriesData5,
        });

        // 更新碎石图数据
        this.screeX = pca_result.data.result.scree_x || [];
        this.screeBar = pca_result.data.result.scree_bar || [];
        this.screePlot = pca_result.data.result.scree_plot || [];

        console.log("碎石图数据已更新:", {
          screeX: this.screeX,
          screeBar: this.screeBar,
          screePlot: this.screePlot,
        });

        this.$message.success("主成分分析完成");
        this.currentStep = 4; // 更新到训练模型步骤
      } catch (error) {
        console.error("PCA分析失败:", error);
        this.$message.error(
          "主成分分析失败: " + (error.response?.data?.message || error.message)
        );
      }
    },
    handleSingleSelect(selectedIndex) {
      this.wavelengthChooseList.forEach((item, index) => {
        item.enabled = index === selectedIndex;
      });
    },
    saveModelDetailData() {
      if (!this.modelDetail_data) {
        this.modelDetail_data = {};
      }

      // 1. 碎石图 screePlot
      this.modelDetail_data.screePlot = JSON.stringify({
        x: this.screeX,
        bar: this.screeBar,
        line: this.screePlot,
      });

      // 2. 负载图 loadingPlot
      this.modelDetail_data.loadingPlot = JSON.stringify({
        xAxis: this.xAxisData5,
        series: this.seriesData5,
      });

      // 3. 得分图 scorePlot
      this.modelDetail_data.scorePlot = JSON.stringify({
        pc1: this.scoreScatterPc1,
        pc2: this.scoreScatterPc2,
        pc1Value: this.pc1Value,
        pc2Value: this.pc2Value,
        ellipseAngle: this.ellipseAngle,
        ellipseWidth: this.ellipseWidth,
        ellipseHeight: this.ellipseHeight,
      });
      this.modelDetail_data.modelId = parseInt(this.modelId) || this.modelId;
      this.modelDetail_data.modelNo = this.modelNo || "";
      this.modelDetail_data.constituentId =
        this.selectedModelDetail.constituentId || 0;
      this.modelDetail_data.coef =
        JSON.stringify(this.selectedModelDetail.coef) || "";
      this.modelDetail_data.intercept = this.selectedModelDetail.intercept || 0;

      this.modelDetail_data.startWavelength = this.startWavelength;
      this.modelDetail_data.endWavelength = this.endWavelength;

      addModelDetail(this.modelDetail_data).then((res) => {
        if (res.code === 200) {
          this.$message({
            message: "模型明细保存成功",
            type: "success",
          });
        } else {
          this.$message({
            message: "模型明细保存失败",
            type: "error",
          });
        }
      });
    },
    handleWaveLengthChoose() {
      this.confirmStepAction(
        "您已经进行到后续步骤，重新进行波长选择将清空后续所有数据，是否继续？",
        2, // 目标步骤为2（波长选择）
        () => {
          this.wavelengthChooseVisible = true;
        }
      );
    },
    preProcessing() {
      const enabledMethods = this.methodList.filter((item) => item.enabled);

      if (enabledMethods.length === 0) {
        this.$message.warning("请至少选择一种预处理方法");
        return;
      }

      // 校验参数
      for (const item of enabledMethods) {
        if (this.needsParam(item.code)) {
          if (!item.param1 || !item.param2) {
            this.$message.error(`方法「${item.name}」参数不能为空`);
            return;
          }
        }
      }

      // 添加预处理开始提示
      this.$message.info("正在执行数据预处理，请稍候...");

      // 构造最终结构（预处理时不包含过程参数）
      const preprocessing_sequence = enabledMethods.map((item) =>
        this.buildMethodPayload(item, false)
      );

      console.log(
        "🚀 最终 preprocessing_sequence:",
        JSON.stringify({ preprocessing_sequence }, null, 2)
      );

      // 模拟预处理结果 - 这里应该调用后端API
      this.simulatePreprocessingResult();

      this.currentStep = 2; // 更新到波长选择步骤
      this.preProcessdialogVisible = false;
    },

    handleDragStart(index) {
      this.dragStartIndex = index;
    },
    handleDrop(dropIndex) {
      const item = this.methodList[this.dragStartIndex];
      this.methodList.splice(this.dragStartIndex, 1);
      this.methodList.splice(dropIndex, 0, item);
    },
    // 监听 checkbox 状态改变
    watchMethodChange() {
      this.methodList.forEach((item) => {
        this.$watch(
          () => item.enabled,
          (newVal) => {
            if (newVal) {
              const payload = this.buildMethodPayload(item);
              console.log("✅ 启用方法:", JSON.stringify(payload, null, 2));
            }
          }
        );
      });
    },
    buildMethodPayload(item, includeProcessParams = false) {
      const payload = {
        root: {
          type: item.type, // 如 scaling、smoothing、baseline
          method: item.e_Name, // 如 normalization、sg_smoothing
        },
      };

      // 处理用户输入参数（预处理前确定的参数）
      if (this.needsParam(item.code)) {
        payload.root.params = {
          window: Number(item.param1),
          polyorder: Number(item.param2),
        };

        // SG导数需要额外的导数阶数参数
        if (item.code === "03" || item.code === "04") {
          payload.root.params.deriv_order = item.code === "03" ? 1 : 2;
        }
      }

      // 差分导数只需要导数阶数参数
      if (item.code === "01" || item.code === "02") {
        payload.root.params = {
          deriv_order: item.code === "01" ? 1 : 2,
        };
      }

      // 如果需要包含过程参数（用于验证集预处理）
      if (includeProcessParams && this.preProcessing_result) {
        if (!payload.root.params) {
          payload.root.params = {};
        }

        // 根据方法类型添加对应的过程参数
        switch (item.code) {
          case "05": // SNV - 通常不需要过程参数
            break;
          case "06": // MSC
            if (this.preProcessing_result.x_msc_mean) {
              console.log(
                "MSC x_mean:",
                JSON.stringify(this.preProcessing_result.x_msc_mean, null, 2)
              );
              payload.root.params.x_mean = this.preProcessing_result.x_msc_mean;
            }
            break;
          case "08": // 均值中心化
            if (this.preProcessing_result.x_center_mean) {
              console.log(
                "Center x_mean:",
                JSON.stringify(
                  this.preProcessing_result.x_center_mean[0],
                  null,
                  2
                )
              );
              payload.root.params.x_mean =
                this.preProcessing_result.x_center_mean[0];
            }
            break;
          case "09": // 归一化
            if (
              this.preProcessing_result.x_nor_max &&
              this.preProcessing_result.x_nor_min
            ) {
              payload.root.params.data_max =
                this.preProcessing_result.x_nor_max;
              payload.root.params.data_min =
                this.preProcessing_result.x_nor_min;
            }
            break;
          case "10": // 标准化
            if (
              this.preProcessing_result.x_standar_mean &&
              this.preProcessing_result.x_standar_scale
            ) {
              payload.root.params.x_mean =
                this.preProcessing_result.x_standar_mean;
              payload.root.params.scale =
                this.preProcessing_result.x_standar_scale;
            }
            break;
        }
      }

      return payload;
    },
    // 判断是否需要参数
    needsParam(code) {
      return ["03", "04", "07"].includes(code);
    },

    // 构建波长选择参数
    buildWaveSelectParams(selectedMethod) {
      const params = {};

      // 根据不同的方法构建不同的参数结构
      switch (selectedMethod.method) {
        case "SiPLS":
          params.n_intervals = Number(selectedMethod.params[0]) || 3;
          params.n_select = Number(selectedMethod.params[1]) || 2;
          params.n_components = Number(selectedMethod.params[2]) || 2;
          break;
        case "VUE":
          params.n_features = Number(selectedMethod.params[0]) || 20;
          params.n_iter = Number(selectedMethod.params[1]) || 10;
          break;
        case "CORRELATION":
          params.threshold = Number(selectedMethod.params[0]) || 0;
          break;
        case "CARS":
          params.n_features = Number(selectedMethod.params[0]) || 20;
          params.n_iter = Number(selectedMethod.params[1]) || 10;
          break;
        case "VIPSelection":
          params.n_features = Number(selectedMethod.params[0]) || 20;
          break;
        default:
          // 默认情况下，将非空参数按顺序添加
          selectedMethod.params.forEach((param, index) => {
            if (param && param.trim() !== "") {
              params[`param${index + 1}`] = isNaN(param)
                ? param
                : Number(param);
            }
          });
      }

      return params;
    },
    handlePreProcess() {
      this.confirmStepAction(
        "您已经进行到后续步骤，重新进行数据预处理将清空后续所有数据，是否继续？",
        1, // 目标步骤为1（数据预处理）
        () => {
          this.preProcessdialogVisible = true;
        }
      );
    },
    getCalibration() {
      if (!this.selectedConstructData) {
        this.$message({
          message: "请先选择校正集数据",
          type: "warning",
        });
        return;
      }
      // 获取建模报告环境光谱数据 以及 异常剔除信息
      // TODO 根据selectedConstructData.constructNo获取建模报告环境光谱数据
      // this.getReportDataBackgroundSpectrum();
      // 获取基团信息
      this.fetchGroupInformation();
      listConstructDetail({
        constructNo: this.selectedConstructData.constructNo,
        constructdTypeName: "校正集",
        pageNum: 1,
        pageSize: 9999, // 设置一个足够大的数值
      }).then((res) => {
        if (res.code === 200 && res.rows.length > 0) {
          // this.modelDetailtableData = res.rows;
          console.log("校正集数据：", res);
          // console.log("校正集数据：", res.rows);
          // 处理校正集数据，将standard和wavelength数据组合成一个数组
          this.preProcessing_waveData = res.rows.map((row) => {
            const wavelengthArray = row.wavelength.split(",").map(Number);
            const standardValue = Number(row.standard);
            this.standardValueList.push(standardValue);
            // console.log("this.standardValueList:", this.standardValueList);
            // 将标准值和波长数据合并成一个数组：[标准值, 波长1, 波长2, ...]
            return [standardValue, ...wavelengthArray];
          });
          console.log("处理后的数据格式:", this.preProcessing_waveData);
          // 根据建模数据编号到建模明细表查询出校正集
          this.$message({
            message: `已选择校正集数据：${this.selectedConstructData.constructNo}`,
            type: "success",
          });

          this.currentStep = 1; // 更新步骤到数据预处理
          this.calibrationdialogVisible = false; // 关闭对话框

          const newSeries = [];
          let xAxisSet = new Set();

          for (const row of res.rows) {
            const start = Number(row.startWavelength);
            const end = Number(row.endWavelength);
            const waveStr = row.wavelength;
            const waveArr = waveStr.split(",").map(Number);

            // 使用 this.startWavelength 作为X轴起点
            const x = Array.from({ length: end - start }, (_, i) =>
              (this.startWavelength + i).toString()
            );
            x.forEach((w) => xAxisSet.add(w));

            newSeries.push({
              name: row.constructDetailNo,
              type: "line",
              data: waveArr,
            });
          }

          this.seriesData1 = newSeries;
          this.xAxisData1 = Array.from(xAxisSet).sort((a, b) => a - b);
          // this.startWavelength  应该作为x轴的起点
          console.log("seriesData1", this.seriesData1);
          console.log("xAxisData1", this.xAxisData1);
        } else {
          this.$message({
            message: "校正集数据加载失败",
            type: "error",
          });
        }
      });
      listConstructDetail({
        constructNo: this.selectedConstructData.constructNo,
        constructdTypeName: "验证集",
        pageNum: 1,
        pageSize: 9999, // 设置一个足够大的数值
      }).then((res) => {
        if (res.code === 200 && res.rows.length > 0) {
          this.trainPayload.test_x = res.rows.map((row) =>
            row.wavelength.split(",").map(Number)
          );
          this.trainPayload.test_y = res.rows.map((row) => row.standard);
          console.log("this.trainPayload收集验证集数据:", this.trainPayload);
        } else {
          this.$message({
            message: "验证集数据加载失败",
            type: "error",
          });
        }
      });
    },
    handleCalibrationSelection(selection) {
      const constructTableRef = this.$refs.constructTable.$refs.constructTable;
      if (selection.length > 1) {
        const lastSelected = selection[selection.length - 1];
        this.$nextTick(() => {
          constructTableRef.clearSelection(); // 清除所有选中
          constructTableRef.toggleRowSelection(lastSelected, true); // 仅保留当前选中
        });
        this.selectedConstructData = lastSelected;
      } else {
        this.selectedConstructData = selection[0] || null;
      }
      this.startWavelength = this.selectedConstructData
        ? this.selectedConstructData.startWavelength
        : 0;
      this.endWavelength = this.selectedConstructData
        ? this.selectedConstructData.endWavelength
        : 0;
      // 建模报告相关字段获取
      this.reportData.modelBaseInfo.constituentName = this.selectedConstructData
        ? this.selectedConstructData.constituentName
        : "";
      this.reportData.modelConstructInfo.sampleProperty = this
        .selectedConstructData
        ? this.selectedConstructData.constituentName
        : "";
      this.reportData.modelConstructInfo.samplePreparationMethod = this
        .selectedConstructData
        ? this.selectedConstructData.preparationMethod
        : "";
      this.reportData.modelConstructInfo.totalSampleCount = this
        .selectedConstructData
        ? this.selectedConstructData.sampleTotal
        : "";
      this.reportData.modelConstructInfo.spectralSamplingInstrument = this
        .selectedConstructData
        ? this.selectedConstructData.instrumentName
        : "";
      this.reportData.modelConstructInfo.standardValueInstrument = this
        .selectedConstructData
        ? this.selectedConstructData.standardInstrument
        : "";
      this.reportData.modelConstructInfo.standardValueMethod = this
        .selectedConstructData
        ? this.selectedConstructData.standardMethod
        : "";
      this.reportData.modelConstructInfo.standardValue_max = this
        .selectedConstructData
        ? this.selectedConstructData.standardMax
        : "";
      this.reportData.modelConstructInfo.standardValue_min = this
        .selectedConstructData
        ? this.selectedConstructData.standardMin
        : "";
      console.log("查看选中的建模数据信息", this.selectedConstructData);
    },
    chooseCalibration() {
      this.calibrationdialogVisible = false;
      // 这里可以添加处理选择校正集的逻辑
      this.$message({
        message: "校正集已选择",
        type: "success",
      });
    },
    // 通用的步骤确认方法
    confirmStepAction(message, targetStep, action) {
      // 检查是否已经进行到后续步骤
      if (this.currentStep > targetStep) {
        this.$confirm(message, "提示", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning",
        })
          .then(() => {
            // 用户确认后，同步修改步骤变量
            this.currentStep = targetStep;
            action();
          })
          .catch(() => {
            this.$message.info("操作已取消");
          });
      } else {
        // 如果当前步骤不高于目标步骤，直接执行操作
        action();
      }
    },

    handleImportCalibration() {
      this.confirmStepAction(
        "您已经进行到后续步骤，重新导入校正集将清空后续所有数据，是否继续？",
        0, // 目标步骤为0（导入校正集）
        () => {
          this.calibrationdialogVisible = true;
        }
      );
    },
    loadAllCharts() {
      // 示例：这里你可以调用各自的 API 或函数分别填充数据
      this.xAxisData1 = this.genXAxis();
      this.seriesData1 = [
        { name: "A", data: [7.6, 7.5, 7.7, 7.8, 7.81, 7.9, 8.25] },
      ];

      this.xAxisData2 = this.genXAxis();
      this.seriesData2 = [
        { name: "B", data: [7.5, 7.4, 7.6, 7.85, 7.9, 8.0, 8.2] },
      ];

      this.xAxisData3 = this.genXAxis();
      this.seriesData3 = [
        { name: "C", data: [7.4, 7.3, 7.5, 7.7, 7.8, 8.0, 8.3] },
      ];

      this.xAxisData4 = this.genXAxis();
      this.seriesData4 = [
        { name: "Scree", data: [7.3, 7.2, 7.6, 7.8, 7.85, 8.0, 8.2] },
      ];

      this.xAxisData5 = this.genXAxis();
      this.seriesData5 = [
        { name: "Load", data: [7.5, 7.3, 7.6, 7.85, 7.95, 8.1, 8.25] },
      ];

      this.xAxisData6 = this.genXAxis();
      this.seriesData6 = [
        { name: "Score", data: [7.6, 7.5, 7.7, 7.8, 7.9, 8.1, 8.3] },
      ];
    },
    genXAxis() {
      return [
        "09:43:00",
        "09:43:10",
        "09:43:20",
        "09:43:30",
        "09:43:40",
        "09:43:50",
        "09:44:00",
      ];
    },
    cancel() {
      this.calibrationdialogVisible = false;
    },

    // 模拟预处理结果
    async simulatePreprocessingResult() {
      if (this.preProcessing_waveData.length === 0) {
        this.$message.warning("请先导入校正集数据");
        return;
      }

      this.preProcessingPayload = {
        preprocessing_sequence: this.methodList
          .filter((item) => item.enabled)
          .map((item) => this.buildMethodPayload(item)),
        wave_data: this.preProcessing_waveData,
      };
      this.pca_req = {
        wave_data: this.preProcessing_waveData,
      };

      console.log("预处理请求数据:", this.preProcessingPayload);

      try {
        // 显示加载提示
        const loadingMessage = this.$message({
          message: "数据预处理中，请稍候...",
          type: "info",
          duration: 0, // 不自动关闭
        });

        const response = await axios.post(
          "/train-model/pretreatment",
          this.preProcessingPayload
        );

        // 关闭加载提示
        loadingMessage.close();

        console.log("预处理结果:", response.data);
        this.preProcessing_result = response.data.result;
        console.log(
          "预处理结果: response.data.result",
          this.preProcessing_result
        );
        // 处理返回的预处理数据，更新图表
        if (response.data && response.data.result.X_preprocessed) {
          this.updatePreprocessedChart(response.data.result.X_preprocessed);
        }
        this.$message.success("数据预处理完成");
      } catch (error) {
        console.error("预处理请求失败:", error);
        this.$message.error(
          "预处理请求失败: " + (error.response?.data?.message || error.message)
        );
      }
    },

    // 波长选择处理
    async handleWavelengthSelection() {
      const selectedMethod = this.wavelengthChooseList.find(
        (item) => item.enabled
      );

      if (!selectedMethod) {
        this.$message.warning("请选择一种波长选择方法");
        return;
      }

      // 验证参数
      const requiredParams = selectedMethod.paramLabels.filter(
        (label) => label !== ""
      );
      for (let i = 0; i < requiredParams.length; i++) {
        if (!selectedMethod.params[i]) {
          this.$message.error(`请填写${requiredParams[i]}参数`);
          return;
        }
      }
      console.log("this.preProcessing_result", this.preProcessing_result);
      // console.log("this.standardValueList", this.standardValueList);

      // 检查预处理结果是否存在
      if (
        !this.preProcessing_result.X_preprocessed ||
        !Array.isArray(this.preProcessing_result.X_preprocessed)
      ) {
        this.$message.error("请先完成数据预处理步骤");
        return;
      }

      if (!this.standardValueList || this.standardValueList.length === 0) {
        this.$message.error("标准值列表为空，请先导入校正集");
        return;
      }

      // 将 standardValueList 的 N 个值分别拼接到 preProcessing_result 的 N 个数组前面
      const wave_data = this.preProcessing_result.X_preprocessed.map(
        (row, index) => {
          return [this.standardValueList[index], ...row];
        }
      );

      console.log("组合后的wave_data:", wave_data);
      console.log("选择的波长选择方法:", selectedMethod);

      // 构建符合后端期望格式的payload
      const payload = {
        wave_select: [
          {
            root: {
              method: selectedMethod.method,
              params: this.buildWaveSelectParams(selectedMethod),
            },
          },
        ],
        wave_data: wave_data, // 使用组合后的数据
      };
      this.waveSelect_data = payload.wave_select;
      console.log("请求数据:", payload);
      // // 模拟波长选择结果
      const response = await axios.post("/train-model/wave-elect", payload);
      // 根据 standValueList 和 preProcessing_result 处理波长选择结果
      console.log("波长选择结果:", response.data);
      this.selected_indices = response.data.result.selected_indices;
      this.mask = response.data.result.mask;

      // 处理波长选择结果并更新图表
      this.processWavelengthSelectionResult(response.data.result);

      this.$message.success("波长选择完成");
      this.currentStep = 3; // 更新到主成分分析步骤
      this.wavelengthChooseVisible = false;
    },

    // 模拟波长选择结果
    simulateWavelengthSelection(method) {
      if (this.seriesData2.length === 0) {
        this.$message.warning("请先完成数据预处理");
        return;
      }

      const payload = {
        root: {
          type: item.type, // 如 scaling、smoothing、baseline
          method: item.e_Name, // 如 normalization、sg_smoothing
        },
      };

      // 模拟波长选择后的数据（选择部分波长）
      const selectedSeries = this.seriesData2.map((series) => ({
        ...series,
        data: series.data.filter((_, index) => index % 3 === 0), // 简单模拟：每3个选1个
      }));

      const selectedXAxis = this.xAxisData2.filter(
        (_, index) => index % 3 === 0
      );

      this.seriesData3 = selectedSeries;
      this.xAxisData3 = selectedXAxis;

      console.log("波长选择后的数据:", {
        series: selectedSeries,
        xAxis: selectedXAxis,
      });
    },

    // 处理波长选择结果并更新图表
    processWavelengthSelectionResult(result) {
      if (!result || !result.selected_indices || !result.mask) {
        console.warn("波长选择结果数据格式不正确");
        return;
      }

      console.log("开始处理波长选择结果...");
      console.log("selected_indices:", result.selected_indices);
      console.log("mask:", result.mask);

      // 保存波长选择结果（仅保存算法结果）
      this.selected_indices = result.selected_indices;
      this.mask = result.mask;

      // 根据掩码筛选出被选中的波长点（仅算法选择的部分）
      if (this.xAxisData2 && this.xAxisData2.length > 0) {
        this.algorithmSelectedWavelengths = this.xAxisData2.filter(
          (wavelength, index) => {
            return result.mask[index] === true || result.mask[index] === 1;
          }
        );
        console.log("算法选择后的波长:", this.algorithmSelectedWavelengths);
      } else {
        // 如果没有预处理的X轴数据，根据掩码生成新的波长数据
        this.algorithmSelectedWavelengths = [];
        result.mask.forEach((isSelected, index) => {
          if (isSelected === true || isSelected === 1) {
            this.algorithmSelectedWavelengths.push(
              (this.startWavelength + index).toString()
            );
          }
        });
        console.log(
          "根据掩码生成的选择后波长:",
          this.algorithmSelectedWavelengths
        );
      }

      // 合并算法选择和基团选择的波长
      this.mergeWavelengthSelections();

      console.log("波长选择结果已更新:", {
        selectedWavelengths: this.chooseWavelength,
        selectedCount: this.chooseWavelength.length,
        selectedIndicesCount: this.selected_indices.length,
      });

      this.$message.success(
        `波长选择完成，共选择了 ${this.chooseWavelength.length} 个波长点`
      );
    },

    // 更新预处理后的图表数据
    updatePreprocessedChart(processedData) {
      if (!processedData || !Array.isArray(processedData)) {
        console.warn("预处理数据格式不正确");
        return;
      }

      const newSeries = [];
      let xAxisSet = new Set();

      processedData.forEach((row, index) => {
        if (Array.isArray(row) && row.length > 1) {
          const [standard, ...wavelengths] = row;
          // 使用 this.startWavelength 作为X轴起点
          const xAxis = wavelengths.map((_, i) =>
            (this.startWavelength + i).toString()
          );
          xAxis.forEach((w) => xAxisSet.add(w));

          newSeries.push({
            name: `Sample ${index + 1}`,
            type: "line",
            data: wavelengths,
          });
        }
      });

      this.seriesData2 = newSeries;
      this.xAxisData2 = Array.from(xAxisSet).sort(
        (a, b) => Number(a) - Number(b)
      );

      console.log("预处理后的图表数据已更新:", {
        series: newSeries,
        xAxis: this.xAxisData2,
      });
    },

    // 显示相关性图
    showCorrelationChart(rowData, latentVariableCount) {
      console.log(
        "点击查看相关性图:",
        rowData,
        "潜在变量数:",
        latentVariableCount
      );

      // 存储当前行数据和潜在变量数
      this.currentRowData = rowData;
      this.currentLatentVariableCount = latentVariableCount;

      // 打开对话框
      this.correlationDialogVisible = true;
    },

    // 相关性图对话框打开时的处理
    onCorrelationDialogOpen() {
      this.$nextTick(() => {
        if (this.$refs.correlationChart) {
          this.$refs.correlationChart.updateChart();
        }
      });
    },

    // 相关性图功能已移至CorrelationChart组件
    // 启用方法
    enableMethod(code) {
      const method = this.methodList.find((item) => item.code === code);
      if (method) {
        method.enabled = true;
        this.$message.success(`已启用: ${method.name}`);
      }
    },

    // 禁用方法
    disableMethod(code) {
      const method = this.methodList.find((item) => item.code === code);
      if (method) {
        method.enabled = false;
        this.$message.info(`已禁用: ${method.name}`);
      }
    },

    // 获取类型标签
    getTypeLabel(type) {
      const typeMap = {
        baseline: "基线校正",
        scatter: "散射校正",
        smoothing: "平滑处理",
        scaling: "尺度缩放",
      };
      return typeMap[type] || type;
    },

    // 拖动开始
    onDragStart(evt) {
      evt.item.classList.add("dragging");
      // 添加视觉反馈
      evt.item.style.opacity = "0.8";
      evt.item.style.transform = "rotate(2deg)";
    },

    // 拖动结束
    onDragEnd(evt) {
      evt.item.classList.remove("dragging");
      // 恢复正常样式
      evt.item.style.opacity = "1";
      evt.item.style.transform = "none";
      this.$message.info("顺序已调整");
    },

    // 重置方法
    resetMethods() {
      this.methodList.forEach((item) => {
        item.enabled = false;
        item.param1 = "";
        item.param2 = "";
      });
      this.$message.info("已重置所有方法");
    },

    // 确认预处理
    confirmPreprocessing() {
      if (this.enabledMethods.length === 0) {
        this.$message.warning("请至少启用一种预处理方法");
        return;
      }

      // 验证参数
      for (const method of this.enabledMethods) {
        if (this.needsParam(method.code)) {
          if (!method.param1 || !method.param2) {
            this.$message.error(`请完善「${method.name}」的参数设置`);
            return;
          }
        }
      }

      this.preProcessing();
    },
  },
};
</script>

<style scoped>
/* 主容器样式 */
.app-container {
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* 固定顶部区域 */
.fixed-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background-color: #fff;
  border-bottom: 1px solid #e4e7ed;
  padding: 10px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

/* 步骤条样式 */
.steps-container {
  margin-bottom: 15px;
}

/* 按钮组样式 */
.button-group {
  border: 1px solid #dcdfe6;
  padding: 10px 5px;
  border-radius: 4px;
  background-color: #fafafa;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

/* 可滚动内容区域 */
.content-area {
  flex: 1;
  overflow-y: auto;
  padding: 10px;
  background-color: #f5f5f5;
}

.modelDataDialog ::v-deep .el-dialog__body {
  padding: 10px;
  max-height: 20vh; /* 控制整个body区域的最大高度 */
  overflow: auto;
}

.dialog-footer {
  /* width: 100%; */
  position: absolute;
  right: 10px;
  bottom: 10px;
}

.ghost {
  opacity: 0.5;
}
.drag-icon {
  cursor: move;
}

.empty-chart-placeholder {
  width: 100%;
  height: 300px;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  display: flex;
  flex-direction: column;
  background-color: #fafafa;
}

.empty-chart-placeholder .chart-title {
  text-align: center;
  font-size: 14px;
  font-weight: bold;
  padding: 10px;
  color: #333;
  border-bottom: 1px solid #ebeef5;
  background-color: #fff;
}

.empty-chart-placeholder .placeholder-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  color: #909399;
}

.empty-chart-placeholder .placeholder-content i {
  font-size: 48px;
  margin-bottom: 16px;
  color: #c0c4cc;
}

.empty-chart-placeholder .placeholder-content p {
  margin: 0;
  font-size: 14px;
}

/* 相关性图链接样式 */
.correlation-link {
  color: #409eff;
  cursor: pointer;
  text-decoration: underline;
  font-size: 14px;
}

.correlation-link:hover {
  color: #66b1ff;
  text-decoration: underline;
}

/* 相关性图表容器样式 */
.correlation-chart-container {
  height: 400px;
  width: 100%;
}

.preprocess-method-container {
  padding: 20px;
}

.enabled-methods-section,
.available-methods-section {
  margin-bottom: 30px;
}

.enabled-methods-section h4,
.available-methods-section h4 {
  margin-bottom: 15px;
  color: #303133;
  font-weight: 600;
}

.enabled-methods-list {
  min-height: 60px;
  border: 1px dashed #dcdfe6;
  border-radius: 4px;
  padding: 10px;
  background: #fafafa;
}

.method-item {
  display: flex;
  align-items: center;
  padding: 12px;
  margin-bottom: 8px;
  border-radius: 4px;
  transition: all 0.3s ease;
}

.method-item.enabled {
  background: #fff;
  border: 1px solid #e4e7ed;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

.method-item.disabled {
  background: #f8f9fa;
  border: 1px solid #f0f0f0;
  cursor: pointer;
}

.method-item.disabled:hover {
  background: #e6f7ff;
  border-color: #91d5ff;
}

.method-drag-handle {
  display: flex;
  align-items: center;
  margin-right: 12px;
  color: #c0c4cc;
}

.drag-handle {
  cursor: move;
  font-size: 16px;
}

.method-order {
  width: 20px;
  height: 20px;
  background: #409eff;
  color: white;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  margin-left: 8px;
}

.method-content {
  flex: 1;
}

.method-header {
  display: flex;
  justify-content: between;
  align-items: center;
  margin-bottom: 8px;
}

.method-name {
  font-weight: 500;
  flex: 1;
}

.method-params {
  display: flex;
  gap: 8px;
}

.available-methods-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 12px;
}

.method-info {
  flex: 1;
  display: flex;
  flex-direction: column;
}

.method-type-tag {
  font-size: 12px;
  padding: 2px 6px;
  border-radius: 3px;
  margin-top: 4px;
  align-self: flex-start;
}

.method-type-tag.baseline {
  background: #fff7e6;
  color: #fa8c16;
  border: 1px solid #ffd591;
}

.method-type-tag.scatter {
  background: #f6ffed;
  color: #52c41a;
  border: 1px solid #b7eb8f;
}

.method-type-tag.smoothing {
  background: #f0f5ff;
  color: #2f54eb;
  border: 1px solid #adc6ff;
}

.method-type-tag.scaling {
  background: #f9f0ff;
  color: #722ed1;
  border: 1px solid #d3adf7;
}

.empty-tip {
  text-align: center;
  color: #909399;
  padding: 20px;
}

.drag-ghost {
  opacity: 0.5;
  background: #f0f5ff;
}

.drag-chosen {
  background: #e6f7ff;
  border-color: #91d5ff;
}

.dragging {
  transform: rotate(5deg);
}

.footer-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}
</style>
